summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PREUPLOAD.cfg2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java17
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java14
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java23
-rw-r--r--cmds/incidentd/src/IncidentService.cpp6
-rw-r--r--cmds/screencap/screencap.cpp12
-rw-r--r--core/api/current.txt2
-rw-r--r--core/api/system-current.txt24
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/android/app/ActivityOptions.java27
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/app/NotificationManager.java4
-rw-r--r--core/java/android/app/smartspace/SmartspaceTarget.java4
-rw-r--r--core/java/android/companion/AssociatedDevice.java133
-rw-r--r--core/java/android/companion/AssociationInfo.java36
-rw-r--r--core/java/android/companion/AssociationRequest.java52
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java2
-rw-r--r--core/java/android/content/Context.java1
-rw-r--r--core/java/android/content/pm/SharedLibraryInfo.java2
-rw-r--r--core/java/android/content/res/AssetFileDescriptor.java234
-rw-r--r--core/java/android/content/res/Resources.java40
-rw-r--r--core/java/android/credentials/ui/Entry.java116
-rw-r--r--core/java/android/credentials/ui/ProviderData.java52
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java2
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java21
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java62
-rw-r--r--core/java/android/os/BatteryStats.java2
-rw-r--r--core/java/android/preference/SeekBarVolumizer.java18
-rw-r--r--core/java/android/provider/Telephony.java88
-rw-r--r--core/java/android/service/credentials/Action.java32
-rw-r--r--core/java/android/service/credentials/CreateCredentialCallback.java64
-rw-r--r--core/java/android/service/credentials/CreateCredentialRequest.java3
-rw-r--r--core/java/android/service/credentials/CreateCredentialResponse.java3
-rw-r--r--core/java/android/service/credentials/Credential.java100
-rw-r--r--core/java/android/service/credentials/CredentialEntry.java48
-rw-r--r--core/java/android/service/credentials/CredentialProviderException.java65
-rw-r--r--core/java/android/service/credentials/CredentialProviderService.java66
-rw-r--r--core/java/android/service/credentials/CredentialsDisplayContent.java3
-rw-r--r--core/java/android/service/credentials/GetCredentialOption.java96
-rw-r--r--core/java/android/service/credentials/GetCredentialsCallback.java65
-rw-r--r--core/java/android/service/credentials/GetCredentialsRequest.java6
-rw-r--r--core/java/android/service/credentials/GetCredentialsResponse.java19
-rw-r--r--core/java/android/service/credentials/SaveEntry.java34
-rw-r--r--core/java/android/service/dreams/DreamManagerInternal.java6
-rw-r--r--core/java/android/service/dreams/DreamService.java174
-rw-r--r--core/java/android/service/notification/NotificationStats.java10
-rw-r--r--core/java/android/service/voice/HotwordAudioStream.aidl19
-rw-r--r--core/java/android/service/voice/HotwordAudioStream.java447
-rw-r--r--core/java/android/service/voice/HotwordDetectedResult.java65
-rw-r--r--core/java/android/util/FeatureFlagUtils.java8
-rw-r--r--core/java/android/view/DisplayAddress.java4
-rw-r--r--core/java/android/view/InputDevice.java26
-rw-r--r--core/java/android/view/InsetsFrameProvider.java47
-rw-r--r--core/java/android/view/SurfaceControl.java54
-rw-r--r--core/java/android/view/View.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java21
-rw-r--r--core/java/android/view/WindowManager.java7
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java14
-rw-r--r--core/java/android/view/autofill/AutofillManager.java1
-rw-r--r--core/java/android/view/inputmethod/IInputMethodManagerInvoker.java11
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java9
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java22
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethod.aidl2
-rw-r--r--core/java/com/android/internal/util/ObservableServiceConnection.java258
-rw-r--r--core/java/com/android/internal/util/PersistentServiceConnection.java200
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl6
-rw-r--r--core/java/com/android/internal/widget/ResolverDrawerLayout.java60
-rw-r--r--core/jni/AndroidRuntime.cpp6
-rw-r--r--core/jni/android_os_Parcel.cpp11
-rw-r--r--core/jni/android_util_Binder.cpp6
-rw-r--r--core/jni/android_view_InputDevice.cpp10
-rw-r--r--core/jni/android_view_SurfaceControl.cpp43
-rw-r--r--core/res/res/layout/resolver_list.xml1
-rw-r--r--core/res/res/values-af/strings.xml2
-rw-r--r--core/res/res/values-am/strings.xml6
-rw-r--r--core/res/res/values-ar/strings.xml10
-rw-r--r--core/res/res/values-as/strings.xml16
-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.xml5
-rw-r--r--core/res/res/values-bg/strings.xml6
-rw-r--r--core/res/res/values-bn/strings.xml3
-rw-r--r--core/res/res/values-bs/strings.xml2
-rw-r--r--core/res/res/values-ca/strings.xml20
-rw-r--r--core/res/res/values-cs/strings.xml6
-rw-r--r--core/res/res/values-da/strings.xml3
-rw-r--r--core/res/res/values-de/strings.xml6
-rw-r--r--core/res/res/values-el/strings.xml4
-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.xml10
-rw-r--r--core/res/res/values-et/strings.xml6
-rw-r--r--core/res/res/values-eu/strings.xml14
-rw-r--r--core/res/res/values-fa/strings.xml7
-rw-r--r--core/res/res/values-fi/strings.xml6
-rw-r--r--core/res/res/values-fr-rCA/strings.xml18
-rw-r--r--core/res/res/values-fr/strings.xml8
-rw-r--r--core/res/res/values-gl/strings.xml6
-rw-r--r--core/res/res/values-gu/strings.xml2
-rw-r--r--core/res/res/values-hi/strings.xml8
-rw-r--r--core/res/res/values-hr/strings.xml10
-rw-r--r--core/res/res/values-hu/strings.xml2
-rw-r--r--core/res/res/values-hy/strings.xml6
-rw-r--r--core/res/res/values-in/strings.xml6
-rw-r--r--core/res/res/values-is/strings.xml6
-rw-r--r--core/res/res/values-it/strings.xml4
-rw-r--r--core/res/res/values-iw/strings.xml5
-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.xml6
-rw-r--r--core/res/res/values-km/strings.xml5
-rw-r--r--core/res/res/values-kn/strings.xml3
-rw-r--r--core/res/res/values-ko/strings.xml6
-rw-r--r--core/res/res/values-ky/strings.xml5
-rw-r--r--core/res/res/values-lo/strings.xml2
-rw-r--r--core/res/res/values-lt/strings.xml5
-rw-r--r--core/res/res/values-lv/strings.xml8
-rw-r--r--core/res/res/values-mk/strings.xml2
-rw-r--r--core/res/res/values-ml/strings.xml3
-rw-r--r--core/res/res/values-mn/strings.xml2
-rw-r--r--core/res/res/values-mr/strings.xml3
-rw-r--r--core/res/res/values-ms/strings.xml2
-rw-r--r--core/res/res/values-my/strings.xml6
-rw-r--r--core/res/res/values-nb/strings.xml35
-rw-r--r--core/res/res/values-ne/strings.xml6
-rw-r--r--core/res/res/values-nl/strings.xml2
-rw-r--r--core/res/res/values-or/strings.xml3
-rw-r--r--core/res/res/values-pa/strings.xml3
-rw-r--r--core/res/res/values-pl/strings.xml3
-rw-r--r--core/res/res/values-pt-rBR/strings.xml6
-rw-r--r--core/res/res/values-pt-rPT/strings.xml11
-rw-r--r--core/res/res/values-pt/strings.xml6
-rw-r--r--core/res/res/values-ro/strings.xml3
-rw-r--r--core/res/res/values-ru/strings.xml12
-rw-r--r--core/res/res/values-si/strings.xml6
-rw-r--r--core/res/res/values-sk/strings.xml10
-rw-r--r--core/res/res/values-sl/strings.xml5
-rw-r--r--core/res/res/values-sq/strings.xml6
-rw-r--r--core/res/res/values-sr/strings.xml2
-rw-r--r--core/res/res/values-sv/strings.xml6
-rw-r--r--core/res/res/values-sw/strings.xml5
-rw-r--r--core/res/res/values-ta/strings.xml3
-rw-r--r--core/res/res/values-te/strings.xml9
-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.xml6
-rw-r--r--core/res/res/values-uk/strings.xml6
-rw-r--r--core/res/res/values-ur/strings.xml6
-rw-r--r--core/res/res/values-uz/strings.xml5
-rw-r--r--core/res/res/values-vi/strings.xml5
-rw-r--r--core/res/res/values-zh-rCN/strings.xml8
-rw-r--r--core/res/res/values-zh-rHK/strings.xml5
-rw-r--r--core/res/res/values-zh-rTW/strings.xml9
-rw-r--r--core/res/res/values-zu/strings.xml5
-rw-r--r--core/res/res/values/attrs.xml6
-rw-r--r--core/res/res/values/config.xml8
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--core/res/res/xml/irq_device_map.xml50
-rw-r--r--core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java7
-rw-r--r--core/tests/utiltests/Android.bp1
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/ObservableServiceConnectionTest.java226
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/PersistentServiceConnectionTest.java215
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java10
-rw-r--r--libs/WindowManager/Jetpack/window-extensions-release.aarbin30816 -> 30820 bytes
-rw-r--r--libs/WindowManager/Shell/Android.bp3
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml32
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_caption_title.xml4
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml8
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml23
-rw-r--r--libs/WindowManager/Shell/res/layout/caption_window_decoration.xml30
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java251
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java358
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java104
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java165
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java21
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java5
-rw-r--r--libs/androidfw/AssetManager2.cpp7
-rw-r--r--libs/androidfw/ResourceTypes.cpp174
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h10
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp3
-rw-r--r--libs/hwui/Layer.h6
-rw-r--r--libs/hwui/Rect.h9
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.cpp27
-rw-r--r--location/java/android/location/LocationManager.java8
-rw-r--r--media/java/android/media/MediaMuxer.java23
-rw-r--r--media/java/android/media/MediaPlayer.java22
-rw-r--r--media/java/android/media/tv/tuner/TunerVersionChecker.java4
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrPlayback.java31
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrRecorder.java31
-rw-r--r--media/jni/Android.bp2
-rw-r--r--media/jni/android_media_MediaMuxer.cpp6
-rw-r--r--media/jni/android_media_tv_Tuner.cpp15
-rw-r--r--media/jni/tuner/DvrClient.cpp10
-rw-r--r--media/jni/tuner/DvrClient.h5
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java2
-rw-r--r--native/android/surface_control.cpp27
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java2
-rw-r--r--packages/SettingsLib/SpaPrivileged/Android.bp2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt4
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt47
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt90
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml28
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/MobileNetworkTypeIcon.kt43
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/MobileNetworkTypeIcons.kt67
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/SignalIcon.java38
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java34
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java68
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java192
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java10
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileNetworkTypeIconsTest.java59
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java66
-rw-r--r--packages/SystemUI/Android.bp2
-rw-r--r--packages/SystemUI/OWNERS29
-rw-r--r--packages/SystemUI/animation/res/values/ids.xml1
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt480
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt5
-rw-r--r--packages/SystemUI/compose/core/Android.bp3
-rw-r--r--packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt363
-rw-r--r--packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt306
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt33
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java3
-rw-r--r--packages/SystemUI/res-keyguard/layout/footer_actions.xml6
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml1
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml4
-rw-r--r--packages/SystemUI/res/layout/screenshot.xml6
-rw-r--r--packages/SystemUI/res/values-night/dimens.xml3
-rw-r--r--packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml4
-rw-r--r--packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml4
-rw-r--r--packages/SystemUI/res/values-pt/tiles_states_strings.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/res/values/ids.xml6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt10
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt20
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt240
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt204
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java5
-rw-r--r--packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt12
-rw-r--r--packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt20
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java23
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt97
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java27
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java376
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt210
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/ChooserSelector.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java112
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java153
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java169
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayMode.kt100
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java184
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java196
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Restarter.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java119
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/OWNERS5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java213
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/AospPolicyModule.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/TouchableRegionViewController.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/condition/Condition.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java58
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java225
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java71
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java65
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java81
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java135
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt61
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java145
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java70
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt81
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorRefactoredTest.kt74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java126
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt37
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/condition/FakeCondition.java8
-rw-r--r--services/api/current.txt72
-rw-r--r--services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java3
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java11
-rw-r--r--services/companion/java/com/android/server/companion/PersistentDataStore.java16
-rw-r--r--services/core/java/android/os/BatteryStatsInternal.java31
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java29
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueImpl.java4
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueModernImpl.java2
-rw-r--r--services/core/java/com/android/server/am/UserController.java78
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java9
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java27
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java68
-rw-r--r--services/core/java/com/android/server/audio/AudioServiceEvents.java11
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java11
-rw-r--r--services/core/java/com/android/server/audio/FadeOutManager.java3
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java14
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java36
-rw-r--r--services/core/java/com/android/server/audio/RecordingActivityMonitor.java7
-rw-r--r--services/core/java/com/android/server/audio/SoundEffectsHelper.java6
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java3
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java73
-rw-r--r--services/core/java/com/android/server/display/DisplayBrightnessState.java173
-rw-r--r--services/core/java/com/android/server/display/DisplayControl.java16
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java42
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java9
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java9
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java9
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java6
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessModeStrategy.java50
-rw-r--r--services/core/java/com/android/server/dreams/DreamController.java4
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java235
-rw-r--r--services/core/java/com/android/server/input/BatteryController.java39
-rw-r--r--services/core/java/com/android/server/input/PersistentDataStore.java5
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java9
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java119
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodUtils.java26
-rw-r--r--services/core/java/com/android/server/inputmethod/SubtypeUtils.java2
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java174
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java7
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLogger.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerLocal.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerInternal.java12
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java7
-rw-r--r--services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java24
-rw-r--r--services/core/java/com/android/server/pm/pkg/AndroidPackage.java96
-rw-r--r--services/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java33
-rw-r--r--services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageState.java24
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserState.java14
-rw-r--r--services/core/java/com/android/server/pm/pkg/SharedLibrary.java17
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java10
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java8
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java16
-rw-r--r--services/core/java/com/android/server/power/stats/CpuWakeupStats.java455
-rw-r--r--services/core/java/com/android/server/power/stats/IrqDeviceMap.java137
-rw-r--r--services/core/java/com/android/server/timedetector/ConfigurationInternal.java34
-rw-r--r--services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java10
-rw-r--r--services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java6
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorInternal.java35
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java39
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorService.java29
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java6
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java25
-rw-r--r--services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java25
-rw-r--r--services/core/java/com/android/server/timezonedetector/CurrentUserIdentityInjector.java46
-rw-r--r--services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java12
-rw-r--r--services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java6
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java35
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java39
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java26
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java6
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java9
-rw-r--r--services/core/java/com/android/server/utils/EventLogger.java (renamed from services/core/java/com/android/server/audio/AudioEventLogger.java)126
-rw-r--r--services/core/java/com/android/server/utils/OWNERS2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java170
-rw-r--r--services/core/java/com/android/server/wm/SafeActivityOptions.java2
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java3
-rw-r--r--services/core/java/com/android/server/wm/Task.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java4
-rw-r--r--services/core/jni/com_android_server_display_DisplayControl.cpp26
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd1
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/DisplayBrightnessStateTest.java67
-rw-r--r--services/tests/servicestests/Android.bp2
-rw-r--r--services/tests/servicestests/res/xml/irq_device_map_1.xml28
-rw-r--r--services/tests/servicestests/res/xml/irq_device_map_2.xml59
-rw-r--r--services/tests/servicestests/res/xml/irq_device_map_3.xml26
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java64
-rw-r--r--services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt16
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java109
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java175
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java98
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java145
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java67
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java107
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java86
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java78
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java201
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java57
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java96
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java90
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java8
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java27
-rw-r--r--telephony/common/com/android/internal/telephony/util/TelephonyUtils.java56
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java37
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java206
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java7
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt81
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt54
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt15
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt7
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt14
-rw-r--r--tests/Input/src/com/android/test/input/InputDeviceTest.java1
-rw-r--r--tests/TrustTests/Android.bp2
-rw-r--r--tools/processors/immutability/Android.bp1
-rw-r--r--tools/processors/immutability/AndroidTestTemplate.xml32
-rw-r--r--tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt2
-rw-r--r--tools/traceinjection/src/com/android/traceinjection/TraceInjectionClassVisitor.java2
-rw-r--r--tools/traceinjection/src/com/android/traceinjection/TraceInjectionMethodAdapter.java4
600 files changed, 14047 insertions, 5275 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 9a4323a119f0..bded26a8748f 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -27,4 +27,4 @@ hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclu
ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check -i ${REPO_ROOT}/frameworks/base/packages/SystemUI/ktfmt_includes.txt ${PREUPLOAD_FILES}
-ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
+ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES}
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java
index 34fb88bf8627..10fa8b93acaf 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java
@@ -20,28 +20,19 @@ import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import java.util.Arrays;
-import java.util.Collection;
/** Tests the performance of various StringBuilder methods. */
-@RunWith(Parameterized.class)
+@RunWith(AndroidJUnit4.class)
@LargeTest
public class StringBuilderPerfTest {
@Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
- @Parameters(name = "mLength={0}")
- public static Collection<Object[]> data() {
- return Arrays.asList(new Object[][] {{1}, {10}, {100}});
- }
-
- @Parameterized.Parameter(0)
- public int mLength;
+ public int mLength = 100;
@Test
public void timeAppendBoolean() {
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 901796be9064..bedfa7f99d99 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -87,6 +87,7 @@ import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
+import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -116,6 +117,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
+import android.util.IntArray;
import android.util.Log;
import android.util.LongArrayQueue;
import android.util.Pair;
@@ -249,6 +251,7 @@ public class AlarmManagerService extends SystemService {
private ActivityManagerInternal mActivityManagerInternal;
private final EconomyManagerInternal mEconomyManagerInternal;
private PackageManagerInternal mPackageManagerInternal;
+ private BatteryStatsInternal mBatteryStatsInternal;
private RoleManager mRoleManager;
private volatile PermissionManagerServiceInternal mLocalPermissionManager;
@@ -2113,6 +2116,8 @@ public class AlarmManagerService extends SystemService {
LocalServices.getService(AppStandbyInternal.class);
appStandbyInternal.addListener(new AppStandbyTracker());
+ mBatteryStatsInternal = LocalServices.getService(BatteryStatsInternal.class);
+
mRoleManager = getContext().getSystemService(RoleManager.class);
mMetricsHelper.registerPuller(() -> mAlarmStore);
@@ -4783,8 +4788,12 @@ public class AlarmManagerService extends SystemService {
}
final ArraySet<Pair<String, Integer>> triggerPackages =
new ArraySet<>();
+ final IntArray wakeupUids = new IntArray();
for (int i = 0; i < triggerList.size(); i++) {
final Alarm a = triggerList.get(i);
+ if (a.wakeup) {
+ wakeupUids.add(a.uid);
+ }
if (mConstants.USE_TARE_POLICY) {
if (!isExemptFromTare(a)) {
triggerPackages.add(Pair.create(
@@ -4796,6 +4805,11 @@ public class AlarmManagerService extends SystemService {
a.sourcePackage, UserHandle.getUserId(a.creatorUid)));
}
}
+ if (wakeupUids.size() > 0 && mBatteryStatsInternal != null) {
+ mBatteryStatsInternal.noteCpuWakingActivity(
+ BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM, nowELAPSED,
+ wakeupUids.toArray());
+ }
deliverAlarmsLocked(triggerList, nowELAPSED);
mTemporaryQuotaReserve.cleanUpExpiredQuotas(nowELAPSED);
if (mConstants.USE_TARE_POLICY) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
index a1a541f92b38..b2ca3a051e36 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
@@ -337,7 +337,6 @@ public class TareController extends StateController {
removeJobFromBillList(jobStatus, billToJobMap.keyAt(i));
}
}
- addJobToBillList(jobStatus, getRunningBill(jobStatus));
final int uid = jobStatus.getSourceUid();
if (mService.getUidBias(uid) == JobInfo.BIAS_TOP_APP) {
@@ -347,6 +346,7 @@ public class TareController extends StateController {
mTopStartedJobs.add(jobStatus);
// Top jobs won't count towards quota so there's no need to involve the EconomyManager.
} else {
+ addJobToBillList(jobStatus, getRunningBill(jobStatus));
mEconomyManagerInternal.noteOngoingEventStarted(userId, pkgName,
getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
}
@@ -357,9 +357,14 @@ public class TareController extends StateController {
public void unprepareFromExecutionLocked(JobStatus jobStatus) {
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
- mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
- getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
- mTopStartedJobs.remove(jobStatus);
+ // If this method is called, then jobStatus.madeActive was never updated, so don't use it
+ // to determine if the EconomyManager was notified.
+ if (!mTopStartedJobs.remove(jobStatus)) {
+ // If the job was started while the app was top, then the EconomyManager wasn't notified
+ // of the job start.
+ mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
+ getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
+ }
final ArraySet<ActionBill> bills = getPossibleStartBills(jobStatus);
ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
@@ -382,9 +387,13 @@ public class TareController extends StateController {
boolean forUpdate) {
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
- mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
- getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
- mTopStartedJobs.remove(jobStatus);
+ if (!mTopStartedJobs.remove(jobStatus) && jobStatus.madeActive > 0) {
+ // Only note the job stop if we previously told the EconomyManager that the job started.
+ // If the job was started while the app was top, then the EconomyManager wasn't notified
+ // of the job start.
+ mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
+ getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
+ }
ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
mRegisteredBillsAndJobs.get(userId, pkgName);
if (billToJobMap != null) {
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 836801deaa08..5af02f405ed9 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -52,11 +52,7 @@ enum {
#define SKIPPED_DUMPSTATE_SECTIONS { \
1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
1200, 1201, 1202, /* Native, hal, java traces */ \
- 3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010, 3011, 3012, 3013, \
- 3014, 3015, 3016, 3017, 3018, 3019, 3020, 3021, 3022, 3023, 3024, 3027, 3028, 3029, \
- 3030, 3031, 3032, 3033, 3034, 3035, 3036, 3037, 3038, 3039, 3040, 3041, 3042, 3043, \
- 3044, 3045, 3046, 3047, 3048, 3049, 3050, 3051, 3052, 3053, 3054, 3055, 3056, 4000, \
- 4001, /* Dumpsys */ }
+ 3018, /* dumpsys meminfo*/ }
namespace android {
namespace os {
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 44fb6b320102..d7222d248911 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -159,12 +159,14 @@ int main(int argc, char** argv)
}
if (!displayId) { // no diplsay id is specified
- if (ids.size() == 1) {
- displayId = ids.front();
- } else {
- fprintf(stderr, "Please specify a display ID (-d display-id) for multi-display device.\n");
+ displayId = ids.front();
+ if (ids.size() > 1) {
+ fprintf(stderr,
+ "[Warning] Multiple displays were found, but no display id was specified! "
+ "Defaulting to the first display found, however this default is not guaranteed "
+ "to be consistent across captures. A display id should be specified.\n");
+ fprintf(stderr, "A display ID can be specified with the [-d display-id] option.\n");
fprintf(stderr, "See \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n");
- return 1;
}
}
diff --git a/core/api/current.txt b/core/api/current.txt
index b06374615730..967645158d23 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8947,6 +8947,7 @@ package android.companion {
public final class AssociationInfo implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public android.os.Parcelable getAssociatedDevice();
method @Nullable public android.net.MacAddress getDeviceMacAddress();
method @Nullable public String getDeviceProfile();
method @Nullable public CharSequence getDisplayName();
@@ -52022,6 +52023,7 @@ package android.view.accessibility {
field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
field public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 256; // 0x100
field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
+ field public static final int CONTENT_CHANGE_TYPE_ENABLED = 4096; // 0x1000
field public static final int CONTENT_CHANGE_TYPE_ERROR = 2048; // 0x800
field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 134b71a49272..5e1c2347664b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7132,6 +7132,7 @@ package android.media.tv.tuner {
field public static final int TUNER_VERSION_1_0 = 65536; // 0x10000
field public static final int TUNER_VERSION_1_1 = 65537; // 0x10001
field public static final int TUNER_VERSION_2_0 = 131072; // 0x20000
+ field public static final int TUNER_VERSION_3_0 = 196608; // 0x30000
field public static final int TUNER_VERSION_UNKNOWN = 0; // 0x0
}
@@ -7149,6 +7150,7 @@ package android.media.tv.tuner.dvr {
method public long read(@NonNull byte[], long, long);
method public long seek(long);
method public void setFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
+ method public int setPlaybackBufferStatusCheckIntervalHint(long);
method public int start();
method public int stop();
field public static final int PLAYBACK_STATUS_ALMOST_EMPTY = 2; // 0x2
@@ -7164,6 +7166,7 @@ package android.media.tv.tuner.dvr {
method public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter);
method public int flush();
method public void setFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
+ method public int setRecordBufferStatusCheckIntervalHint(long);
method public int start();
method public int stop();
method public long write(long);
@@ -11982,9 +11985,29 @@ package android.service.voice {
method public int getStart();
}
+ public final class HotwordAudioStream implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.media.AudioFormat getAudioFormat();
+ method @NonNull public android.os.ParcelFileDescriptor getAudioStream();
+ method @NonNull public android.os.PersistableBundle getMetadata();
+ method @Nullable public android.media.AudioTimestamp getTimestamp();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordAudioStream> CREATOR;
+ }
+
+ public static final class HotwordAudioStream.Builder {
+ ctor public HotwordAudioStream.Builder(@NonNull android.media.AudioFormat, @NonNull android.os.ParcelFileDescriptor);
+ method @NonNull public android.service.voice.HotwordAudioStream build();
+ method @NonNull public android.service.voice.HotwordAudioStream.Builder setAudioFormat(@NonNull android.media.AudioFormat);
+ method @NonNull public android.service.voice.HotwordAudioStream.Builder setAudioStream(@NonNull android.os.ParcelFileDescriptor);
+ method @NonNull public android.service.voice.HotwordAudioStream.Builder setMetadata(@NonNull android.os.PersistableBundle);
+ method @NonNull public android.service.voice.HotwordAudioStream.Builder setTimestamp(@NonNull android.media.AudioTimestamp);
+ }
+
public final class HotwordDetectedResult implements android.os.Parcelable {
method public int describeContents();
method public int getAudioChannel();
+ method @NonNull public java.util.List<android.service.voice.HotwordAudioStream> getAudioStreams();
method public int getConfidenceLevel();
method @NonNull public android.os.PersistableBundle getExtras();
method public int getHotwordDurationMillis();
@@ -12014,6 +12037,7 @@ package android.service.voice {
ctor public HotwordDetectedResult.Builder();
method @NonNull public android.service.voice.HotwordDetectedResult build();
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setAudioChannel(int);
+ method @NonNull public android.service.voice.HotwordDetectedResult.Builder setAudioStreams(@NonNull java.util.List<android.service.voice.HotwordAudioStream>);
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setConfidenceLevel(int);
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setExtras(@NonNull android.os.PersistableBundle);
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setHotwordDetectionPersonalized(boolean);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e2690a9a7ebf..39a7ca82c639 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2892,7 +2892,6 @@ package android.view {
public final class SurfaceControl implements android.os.Parcelable {
ctor public SurfaceControl(@NonNull android.view.SurfaceControl, @NonNull String);
- method @NonNull public static android.os.IBinder getInternalDisplayToken();
method public boolean isSameSurface(@NonNull android.view.SurfaceControl);
}
@@ -3145,6 +3144,7 @@ package android.view.inputmethod {
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
method public boolean hasActiveInputConnection(@Nullable android.view.View);
method public boolean isInputMethodPickerShown();
+ method @RequiresPermission("android.permission.TEST_INPUT_METHOD") public void setStylusWindowIdleTimeoutForTest(long);
field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index f36220460505..52ef7fbb3eca 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -266,6 +266,12 @@ public class ActivityOptions extends ComponentOptions {
private static final String KEY_LAUNCH_TASK_ID = "android.activity.launchTaskId";
/**
+ * See {@link #setDisableStartingWindow}.
+ * @hide
+ */
+ private static final String KEY_DISABLE_STARTING_WINDOW = "android.activity.disableStarting";
+
+ /**
* See {@link #setPendingIntentLaunchFlags(int)}
* @hide
*/
@@ -477,6 +483,7 @@ public class ActivityOptions extends ComponentOptions {
private PictureInPictureParams mLaunchIntoPipParams;
private boolean mDismissKeyguard;
private boolean mIgnorePendingIntentCreatorForegroundState;
+ private boolean mDisableStartingWindow;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -1284,6 +1291,7 @@ public class ActivityOptions extends ComponentOptions {
mDismissKeyguard = opts.getBoolean(KEY_DISMISS_KEYGUARD);
mIgnorePendingIntentCreatorForegroundState = opts.getBoolean(
KEY_IGNORE_PENDING_INTENT_CREATOR_FOREGROUND_STATE);
+ mDisableStartingWindow = opts.getBoolean(KEY_DISABLE_STARTING_WINDOW);
}
/**
@@ -1700,6 +1708,22 @@ public class ActivityOptions extends ComponentOptions {
}
/**
+ * Sets whether recents disable showing starting window when activity launch.
+ * @hide
+ */
+ @RequiresPermission(START_TASKS_FROM_RECENTS)
+ public void setDisableStartingWindow(boolean disable) {
+ mDisableStartingWindow = disable;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean getDisableStartingWindow() {
+ return mDisableStartingWindow;
+ }
+
+ /**
* Specifies intent flags to be applied for any activity started from a PendingIntent.
*
* @hide
@@ -2210,6 +2234,9 @@ public class ActivityOptions extends ComponentOptions {
b.putBoolean(KEY_IGNORE_PENDING_INTENT_CREATOR_FOREGROUND_STATE,
mIgnorePendingIntentCreatorForegroundState);
}
+ if (mDisableStartingWindow) {
+ b.putBoolean(KEY_DISABLE_STARTING_WINDOW, mDisableStartingWindow);
+ }
return b;
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index ddfbc6847ed0..302d1469e1fb 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -159,6 +159,7 @@ interface INotificationManager
void clearRequestedListenerHints(in INotificationListener token);
void requestHintsFromListener(in INotificationListener token, int hints);
int getHintsFromListener(in INotificationListener token);
+ int getHintsFromListenerNoToken();
void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
int getInterruptionFilterFromListener(in INotificationListener token);
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 392f52a08fb5..f6d27ad08b00 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -66,9 +66,9 @@ import java.util.Objects;
/**
* Class to notify the user of events that happen. This is how you tell
- * the user that something has happened in the background. {@more}
+ * the user that something has happened in the background.
*
- * Notifications can take different forms:
+ * <p>Notifications can take different forms:
* <ul>
* <li>A persistent icon that goes in the status bar and is accessible
* through the launcher, (when the user selects it, a designated Intent
diff --git a/core/java/android/app/smartspace/SmartspaceTarget.java b/core/java/android/app/smartspace/SmartspaceTarget.java
index 79d7b216628f..3c66a15399d3 100644
--- a/core/java/android/app/smartspace/SmartspaceTarget.java
+++ b/core/java/android/app/smartspace/SmartspaceTarget.java
@@ -245,6 +245,10 @@ public final class SmartspaceTarget implements Parcelable {
public static final int UI_TEMPLATE_COMBINED_CARDS = 6;
// Sub-card template whose data is represented by {@link SubCardTemplateData}
public static final int UI_TEMPLATE_SUB_CARD = 7;
+ // Reserved: 8
+ // Template type used by non-UI template features for sending logging information in the
+ // base template data. This should not be used for UI template features.
+ // public static final int UI_TEMPLATE_LOGGING_ONLY = 8;
/**
* The types of the Smartspace ui templates.
diff --git a/core/java/android/companion/AssociatedDevice.java b/core/java/android/companion/AssociatedDevice.java
new file mode 100644
index 000000000000..3758cdb680b1
--- /dev/null
+++ b/core/java/android/companion/AssociatedDevice.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2022 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.companion;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Loose wrapper around device parcelable. Device can be one of three types:
+ *
+ * <ul>
+ * <li>for classic Bluetooth - {@link android.bluetooth.BluetoothDevice}</li>
+ * <li>for Bluetooth LE - {@link android.bluetooth.le.ScanResult}</li>
+ * <li>for WiFi - {@link android.net.wifi.ScanResult}</li>
+ * </ul>
+ *
+ * This class serves as temporary wrapper to deliver a loosely-typed parcelable object from
+ * {@link com.android.companiondevicemanager.CompanionDeviceActivity} to the Companion app,
+ * and should only be used internally.
+ *
+ * @hide
+ */
+public final class AssociatedDevice implements Parcelable {
+ private static final int CLASSIC_BLUETOOTH = 0;
+ private static final int BLUETOOTH_LE = 1;
+ private static final int WIFI = 2;
+
+ @NonNull private final Parcelable mDevice;
+
+ public AssociatedDevice(@NonNull Parcelable device) {
+ mDevice = device;
+ }
+
+ private AssociatedDevice(Parcel in) {
+ Creator<? extends Parcelable> creator = getDeviceCreator(in.readInt());
+ mDevice = creator.createFromParcel(in);
+ }
+
+ /**
+ * Return device info. Cast to expected device type.
+ */
+ @NonNull
+ public Parcelable getDevice() {
+ return mDevice;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // Parcel device type with int for efficiency
+ dest.writeInt(getDeviceType());
+ mDevice.writeToParcel(dest, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private int getDeviceType() {
+ if (mDevice instanceof android.bluetooth.BluetoothDevice) return CLASSIC_BLUETOOTH;
+ if (mDevice instanceof android.bluetooth.le.ScanResult) return BLUETOOTH_LE;
+ if (mDevice instanceof android.net.wifi.ScanResult) return WIFI;
+ throw new UnsupportedOperationException("Unsupported device type.");
+ }
+
+ private static Creator<? extends Parcelable> getDeviceCreator(int deviceType) {
+ switch (deviceType) {
+ case CLASSIC_BLUETOOTH: return android.bluetooth.BluetoothDevice.CREATOR;
+ case BLUETOOTH_LE: return android.bluetooth.le.ScanResult.CREATOR;
+ case WIFI: return android.net.wifi.ScanResult.CREATOR;
+ default: throw new UnsupportedOperationException("Unsupported device type.");
+ }
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<AssociatedDevice> CREATOR =
+ new Parcelable.Creator<AssociatedDevice>() {
+ @Override
+ public AssociatedDevice[] newArray(int size) {
+ return new AssociatedDevice[size];
+ }
+
+ @Override
+ public AssociatedDevice createFromParcel(@NonNull Parcel in) {
+ return new AssociatedDevice(in);
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "AssociatedDevice { "
+ + "device = " + mDevice
+ + " }";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AssociatedDevice that = (AssociatedDevice) o;
+ if (getDeviceType() != that.getDeviceType()) return false;
+
+ // TODO(b/31972115): Take out this whole part ¯\_(ツ)_/¯
+ if (mDevice instanceof android.bluetooth.le.ScanResult
+ || mDevice instanceof android.net.wifi.ScanResult) {
+ return mDevice.toString().equals(that.mDevice.toString());
+ }
+
+ return java.util.Objects.equals(mDevice, that.mDevice);
+ }
+
+ @Override
+ public int hashCode() {
+ return java.util.Objects.hash(mDevice);
+ }
+}
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 93748f81ffa1..93964b3f4180 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -52,6 +52,7 @@ public final class AssociationInfo implements Parcelable {
private final @Nullable MacAddress mDeviceMacAddress;
private final @Nullable CharSequence mDisplayName;
private final @Nullable String mDeviceProfile;
+ private final @Nullable AssociatedDevice mAssociatedDevice;
private final boolean mSelfManaged;
private final boolean mNotifyOnDeviceNearby;
@@ -78,8 +79,9 @@ public final class AssociationInfo implements Parcelable {
*/
public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName,
@Nullable MacAddress macAddress, @Nullable CharSequence displayName,
- @Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby,
- boolean revoked, long timeApprovedMs, long lastTimeConnectedMs) {
+ @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice,
+ boolean selfManaged, boolean notifyOnDeviceNearby, boolean revoked,
+ long timeApprovedMs, long lastTimeConnectedMs) {
if (id <= 0) {
throw new IllegalArgumentException("Association ID should be greater than 0");
}
@@ -96,6 +98,7 @@ public final class AssociationInfo implements Parcelable {
mDeviceMacAddress = macAddress;
mDisplayName = displayName;
mDeviceProfile = deviceProfile;
+ mAssociatedDevice = associatedDevice;
mSelfManaged = selfManaged;
mNotifyOnDeviceNearby = notifyOnDeviceNearby;
@@ -160,6 +163,24 @@ public final class AssociationInfo implements Parcelable {
}
/**
+ * Companion device that was associated. Note that this field is not persisted across sessions.
+ *
+ * Cast to expected device type before use:
+ *
+ * <ul>
+ * <li>for classic Bluetooth - {@link android.bluetooth.BluetoothDevice}</li>
+ * <li>for Bluetooth LE - {@link android.bluetooth.le.ScanResult}</li>
+ * <li>for WiFi - {@link android.net.wifi.ScanResult}</li>
+ * </ul>
+ *
+ * @return the companion device that was associated, or {@code null} if the device is
+ * self-managed.
+ */
+ public @Nullable Parcelable getAssociatedDevice() {
+ return mAssociatedDevice == null ? null : mAssociatedDevice.getDevice();
+ }
+
+ /**
* @return whether the association is managed by the companion application it belongs to.
* @see AssociationRequest.Builder#setSelfManaged(boolean)
* @hide
@@ -260,6 +281,7 @@ public final class AssociationInfo implements Parcelable {
+ ", mDisplayName='" + mDisplayName + '\''
+ ", mDeviceProfile='" + mDeviceProfile + '\''
+ ", mSelfManaged=" + mSelfManaged
+ + ", mAssociatedDevice=" + mAssociatedDevice
+ ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby
+ ", mRevoked=" + mRevoked
+ ", mTimeApprovedMs=" + new Date(mTimeApprovedMs)
@@ -284,14 +306,15 @@ public final class AssociationInfo implements Parcelable {
&& Objects.equals(mPackageName, that.mPackageName)
&& Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
&& Objects.equals(mDisplayName, that.mDisplayName)
- && Objects.equals(mDeviceProfile, that.mDeviceProfile);
+ && Objects.equals(mDeviceProfile, that.mDeviceProfile)
+ && Objects.equals(mAssociatedDevice, that.mAssociatedDevice);
}
@Override
public int hashCode() {
return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
- mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mRevoked, mTimeApprovedMs,
- mLastTimeConnectedMs);
+ mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked,
+ mTimeApprovedMs, mLastTimeConnectedMs);
}
@Override
@@ -309,6 +332,7 @@ public final class AssociationInfo implements Parcelable {
dest.writeTypedObject(mDeviceMacAddress, 0);
dest.writeCharSequence(mDisplayName);
dest.writeString(mDeviceProfile);
+ dest.writeTypedObject(mAssociatedDevice, 0);
dest.writeBoolean(mSelfManaged);
dest.writeBoolean(mNotifyOnDeviceNearby);
@@ -326,6 +350,7 @@ public final class AssociationInfo implements Parcelable {
mDeviceMacAddress = in.readTypedObject(MacAddress.CREATOR);
mDisplayName = in.readCharSequence();
mDeviceProfile = in.readString();
+ mAssociatedDevice = in.readTypedObject(AssociatedDevice.CREATOR);
mSelfManaged = in.readBoolean();
mNotifyOnDeviceNearby = in.readBoolean();
@@ -433,6 +458,7 @@ public final class AssociationInfo implements Parcelable {
mOriginalInfo.mDeviceMacAddress,
mOriginalInfo.mDisplayName,
mOriginalInfo.mDeviceProfile,
+ mOriginalInfo.mAssociatedDevice,
mOriginalInfo.mSelfManaged,
mNotifyOnDeviceNearby,
mRevoked,
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 75ab11531595..a2277e97c043 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -154,6 +154,11 @@ public final class AssociationRequest implements Parcelable {
private @Nullable CharSequence mDisplayName;
/**
+ * The device that was associated. Will be null for "self-managed" association.
+ */
+ private @Nullable AssociatedDevice mAssociatedDevice;
+
+ /**
* Whether the association is to be managed by the companion application.
*/
private final boolean mSelfManaged;
@@ -307,6 +312,11 @@ public final class AssociationRequest implements Parcelable {
}
/** @hide */
+ public void setAssociatedDevice(AssociatedDevice associatedDevice) {
+ mAssociatedDevice = associatedDevice;
+ }
+
+ /** @hide */
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public List<DeviceFilter<?>> getDeviceFilters() {
@@ -439,6 +449,16 @@ public final class AssociationRequest implements Parcelable {
/**
+ * The device that was associated. Will be null for "self-managed" association.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable AssociatedDevice getAssociatedDevice() {
+ return mAssociatedDevice;
+ }
+
+ /**
* The app package name of the application the association will belong to.
* Populated by the system.
*
@@ -503,6 +523,7 @@ public final class AssociationRequest implements Parcelable {
"deviceFilters = " + mDeviceFilters + ", " +
"deviceProfile = " + mDeviceProfile + ", " +
"displayName = " + mDisplayName + ", " +
+ "associatedDevice = " + mAssociatedDevice + ", " +
"selfManaged = " + mSelfManaged + ", " +
"forceConfirmation = " + mForceConfirmation + ", " +
"packageName = " + mPackageName + ", " +
@@ -530,6 +551,7 @@ public final class AssociationRequest implements Parcelable {
&& Objects.equals(mDeviceFilters, that.mDeviceFilters)
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
&& Objects.equals(mDisplayName, that.mDisplayName)
+ && Objects.equals(mAssociatedDevice, that.mAssociatedDevice)
&& mSelfManaged == that.mSelfManaged
&& mForceConfirmation == that.mForceConfirmation
&& Objects.equals(mPackageName, that.mPackageName)
@@ -550,6 +572,7 @@ public final class AssociationRequest implements Parcelable {
_hash = 31 * _hash + Objects.hashCode(mDeviceFilters);
_hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
_hash = 31 * _hash + Objects.hashCode(mDisplayName);
+ _hash = 31 * _hash + Objects.hashCode(mAssociatedDevice);
_hash = 31 * _hash + Boolean.hashCode(mSelfManaged);
_hash = 31 * _hash + Boolean.hashCode(mForceConfirmation);
_hash = 31 * _hash + Objects.hashCode(mPackageName);
@@ -568,17 +591,19 @@ public final class AssociationRequest implements Parcelable {
int flg = 0;
if (mSingleDevice) flg |= 0x1;
- if (mSelfManaged) flg |= 0x10;
- if (mForceConfirmation) flg |= 0x20;
- if (mSkipPrompt) flg |= 0x400;
+ if (mSelfManaged) flg |= 0x20;
+ if (mForceConfirmation) flg |= 0x40;
+ if (mSkipPrompt) flg |= 0x800;
if (mDeviceProfile != null) flg |= 0x4;
if (mDisplayName != null) flg |= 0x8;
- if (mPackageName != null) flg |= 0x40;
- if (mDeviceProfilePrivilegesDescription != null) flg |= 0x100;
+ if (mAssociatedDevice != null) flg |= 0x10;
+ if (mPackageName != null) flg |= 0x80;
+ if (mDeviceProfilePrivilegesDescription != null) flg |= 0x200;
dest.writeInt(flg);
dest.writeParcelableList(mDeviceFilters, flags);
if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
if (mDisplayName != null) dest.writeCharSequence(mDisplayName);
+ if (mAssociatedDevice != null) dest.writeTypedObject(mAssociatedDevice, flags);
if (mPackageName != null) dest.writeString(mPackageName);
dest.writeInt(mUserId);
if (mDeviceProfilePrivilegesDescription != null) dest.writeString(mDeviceProfilePrivilegesDescription);
@@ -598,18 +623,20 @@ public final class AssociationRequest implements Parcelable {
int flg = in.readInt();
boolean singleDevice = (flg & 0x1) != 0;
- boolean selfManaged = (flg & 0x10) != 0;
- boolean forceConfirmation = (flg & 0x20) != 0;
- boolean skipPrompt = (flg & 0x400) != 0;
+ boolean selfManaged = (flg & 0x20) != 0;
+ boolean forceConfirmation = (flg & 0x40) != 0;
+ boolean skipPrompt = (flg & 0x800) != 0;
List<DeviceFilter<?>> deviceFilters = new ArrayList<>();
in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader(),
(Class<android.companion.DeviceFilter<?>>) (Class<?>)
android.companion.DeviceFilter.class);
String deviceProfile = (flg & 0x4) == 0 ? null : in.readString();
CharSequence displayName = (flg & 0x8) == 0 ? null : (CharSequence) in.readCharSequence();
- String packageName = (flg & 0x40) == 0 ? null : in.readString();
+ AssociatedDevice associatedDevice = (flg & 0x10) == 0
+ ? null : (AssociatedDevice) in.readTypedObject(AssociatedDevice.CREATOR);
+ String packageName = (flg & 0x80) == 0 ? null : in.readString();
int userId = in.readInt();
- String deviceProfilePrivilegesDescription = (flg & 0x100) == 0 ? null : in.readString();
+ String deviceProfilePrivilegesDescription = (flg & 0x200) == 0 ? null : in.readString();
long creationTime = in.readLong();
this.mSingleDevice = singleDevice;
@@ -620,6 +647,7 @@ public final class AssociationRequest implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
DeviceProfile.class, null, mDeviceProfile);
this.mDisplayName = displayName;
+ this.mAssociatedDevice = associatedDevice;
this.mSelfManaged = selfManaged;
this.mForceConfirmation = forceConfirmation;
this.mPackageName = packageName;
@@ -648,10 +676,10 @@ public final class AssociationRequest implements Parcelable {
};
@DataClass.Generated(
- time = 1649179640045L,
+ time = 1663088980513L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
- inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_COMPUTER\nprivate final boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.UserIdInt int mUserId\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate final long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic boolean isSelfManaged()\npublic boolean isForceConfirmation()\npublic boolean isSingleDevice()\npublic void setPackageName(java.lang.String)\npublic void setUserId(int)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic void setDisplayName(java.lang.CharSequence)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genConstDefs=false)")
+ inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_COMPUTER\nprivate final boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate @android.annotation.Nullable android.companion.AssociatedDevice mAssociatedDevice\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.UserIdInt int mUserId\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate final long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic boolean isSelfManaged()\npublic boolean isForceConfirmation()\npublic boolean isSingleDevice()\npublic void setPackageName(java.lang.String)\npublic void setUserId(int)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic void setDisplayName(java.lang.CharSequence)\npublic void setAssociatedDevice(android.companion.AssociatedDevice)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 4142bceb2c45..90973c307a20 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -145,7 +145,7 @@ public final class CompanionDeviceManager {
* <li>for WiFi - {@link android.net.wifi.ScanResult}</li>
* </ul>
*
- * @deprecated use {@link #EXTRA_ASSOCIATION} instead.
+ * @deprecated use {@link AssociationInfo#getAssociatedDevice()} instead.
*/
@Deprecated
public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cb5a99fcc811..7b352133e16b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3817,6 +3817,7 @@ public abstract class Context {
CAPTIONING_SERVICE,
KEYGUARD_SERVICE,
LOCATION_SERVICE,
+ HEALTHCONNECT_SERVICE,
//@hide: COUNTRY_DETECTOR,
SEARCH_SERVICE,
SENSOR_SERVICE,
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 4c0e2e6b36ca..fdd2aa1fe5fa 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -49,7 +49,7 @@ public final class SharedLibraryInfo implements Parcelable {
TYPE_SDK_PACKAGE,
})
@Retention(RetentionPolicy.SOURCE)
- @interface Type{}
+ public @interface Type{}
/**
* Shared library type: this library is a part of the OS
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index ac65933db136..d2a6f03cc1a1 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -21,12 +21,20 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.system.ErrnoException;
+import android.system.Os;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
/**
* File descriptor of an entry in the AssetManager. This provides your own
@@ -203,19 +211,26 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
*/
public static class AutoCloseInputStream
extends ParcelFileDescriptor.AutoCloseInputStream {
- private long mRemaining;
+ /** Size of current file. */
+ private long mTotalSize;
+ /** The absolute position of current file start point. */
+ private final long mFileOffset;
+ /** The relative position where input stream is against mFileOffset. */
+ private long mOffset;
+ private OffsetCorrectFileChannel mOffsetCorrectFileChannel;
public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
super(fd.getParcelFileDescriptor());
- super.skip(fd.getStartOffset());
- mRemaining = (int) fd.getLength();
+ mTotalSize = fd.getLength();
+ mFileOffset = fd.getStartOffset();
}
@Override
public int available() throws IOException {
- return mRemaining >= 0
- ? (mRemaining < 0x7fffffff ? (int) mRemaining : 0x7fffffff)
- : super.available();
+ long available = mTotalSize - mOffset;
+ return available >= 0
+ ? (available < 0x7fffffff ? (int) available : 0x7fffffff)
+ : 0;
}
@Override
@@ -227,15 +242,24 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
@Override
public int read(byte[] buffer, int offset, int count) throws IOException {
- if (mRemaining >= 0) {
- if (mRemaining == 0) return -1;
- if (count > mRemaining) count = (int) mRemaining;
- int res = super.read(buffer, offset, count);
- if (res >= 0) mRemaining -= res;
- return res;
+ int available = available();
+ if (available <= 0) {
+ return -1;
}
- return super.read(buffer, offset, count);
+ if (count > available) count = available;
+ try {
+ int res = Os.pread(getFD(), buffer, offset, count, mFileOffset + mOffset);
+ // pread returns 0 at end of file, while java's InputStream interface requires -1
+ if (res == 0) res = -1;
+ if (res > 0) {
+ mOffset += res;
+ updateChannelPosition(mOffset + mFileOffset);
+ }
+ return res;
+ } catch (ErrnoException e) {
+ throw new IOException(e);
+ }
}
@Override
@@ -245,41 +269,185 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
@Override
public long skip(long count) throws IOException {
- if (mRemaining >= 0) {
- if (mRemaining == 0) return -1;
- if (count > mRemaining) count = mRemaining;
- long res = super.skip(count);
- if (res >= 0) mRemaining -= res;
- return res;
+ int available = available();
+ if (available <= 0) {
+ return -1;
}
- return super.skip(count);
+ if (count > available) count = available;
+ mOffset += count;
+ updateChannelPosition(mOffset + mFileOffset);
+ return count;
}
@Override
public void mark(int readlimit) {
- if (mRemaining >= 0) {
- // Not supported.
- return;
- }
- super.mark(readlimit);
+ // Not supported.
+ return;
}
@Override
public boolean markSupported() {
- if (mRemaining >= 0) {
- return false;
- }
- return super.markSupported();
+ return false;
}
@Override
public synchronized void reset() throws IOException {
- if (mRemaining >= 0) {
- // Not supported.
- return;
+ // Not supported.
+ return;
+ }
+
+ @Override
+ public FileChannel getChannel() {
+ if (mOffsetCorrectFileChannel == null) {
+ mOffsetCorrectFileChannel = new OffsetCorrectFileChannel(super.getChannel());
+ }
+ try {
+ updateChannelPosition(mOffset + mFileOffset);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return mOffsetCorrectFileChannel;
+ }
+
+ /**
+ * Update the position of mOffsetCorrectFileChannel only after it is constructed.
+ *
+ * @param newPosition The absolute position mOffsetCorrectFileChannel needs to be moved to.
+ */
+ private void updateChannelPosition(long newPosition) throws IOException {
+ if (mOffsetCorrectFileChannel != null) {
+ mOffsetCorrectFileChannel.position(newPosition);
+ }
+ }
+
+ /**
+ * A FileChannel wrapper that will update mOffset of the AutoCloseInputStream
+ * to correct position when using FileChannel to read. All occurrence of position
+ * should be using absolute solution and each override method just do Delegation
+ * besides additional check. All methods related to write mode have been disabled
+ * and will throw UnsupportedOperationException with customized message.
+ */
+ private class OffsetCorrectFileChannel extends FileChannel {
+ private final FileChannel mDelegate;
+ private static final String METHOD_NOT_SUPPORTED_MESSAGE =
+ "This Method is not supported in AutoCloseInputStream FileChannel.";
+
+ OffsetCorrectFileChannel(FileChannel fc) {
+ mDelegate = fc;
+ }
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ if (available() <= 0) return -1;
+ int bytesRead = mDelegate.read(dst);
+ if (bytesRead != -1) mOffset += bytesRead;
+ return bytesRead;
+ }
+
+ @Override
+ public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
+ if (available() <= 0) return -1;
+ if (mOffset + length > mTotalSize) {
+ length = (int) (mTotalSize - mOffset);
+ }
+ long bytesRead = mDelegate.read(dsts, offset, length);
+ if (bytesRead != -1) mOffset += bytesRead;
+ return bytesRead;
+ }
+
+ @Override
+ /**The only read method that does not move channel position*/
+ public int read(ByteBuffer dst, long position) throws IOException {
+ if (position - mFileOffset > mTotalSize) return -1;
+ return mDelegate.read(dst, position);
+ }
+
+ @Override
+ public long position() throws IOException {
+ return mDelegate.position();
+ }
+
+ @Override
+ public FileChannel position(long newPosition) throws IOException {
+ mOffset = newPosition - mFileOffset;
+ return mDelegate.position(newPosition);
+ }
+
+ @Override
+ public long size() throws IOException {
+ return mTotalSize;
+ }
+
+ @Override
+ public long transferTo(long position, long count, WritableByteChannel target)
+ throws IOException {
+ if (position - mFileOffset > mTotalSize) {
+ return 0;
+ }
+ if (position - mFileOffset + count > mTotalSize) {
+ count = mTotalSize - (position - mFileOffset);
+ }
+ return mDelegate.transferTo(position, count, target);
+ }
+
+ @Override
+ public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
+ if (position - mFileOffset > mTotalSize) {
+ throw new IOException(
+ "Cannot map to buffer because position exceed current file size.");
+ }
+ if (position - mFileOffset + size > mTotalSize) {
+ size = mTotalSize - (position - mFileOffset);
+ }
+ return mDelegate.map(mode, position, size);
+ }
+
+ @Override
+ protected void implCloseChannel() throws IOException {
+ mDelegate.close();
+ }
+
+ @Override
+ public int write(ByteBuffer src) throws IOException {
+ throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
+ throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public int write(ByteBuffer src, long position) throws IOException {
+ throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public long transferFrom(ReadableByteChannel src, long position, long count)
+ throws IOException {
+ throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public FileChannel truncate(long size) throws IOException {
+ throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public void force(boolean metaData) throws IOException {
+ throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public FileLock lock(long position, long size, boolean shared) throws IOException {
+ throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public FileLock tryLock(long position, long size, boolean shared) throws IOException {
+ throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
}
- super.reset();
}
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index a03286d3ec6f..fb1fcf8e2a06 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -173,6 +173,7 @@ public class Resources {
* mThemeRefNextFlushSize is reached.
*/
private static final int MIN_THEME_REFS_FLUSH_SIZE = 32;
+ private static final int MAX_THEME_REFS_FLUSH_SIZE = 512;
private int mThemeRefsNextFlushSize = MIN_THEME_REFS_FLUSH_SIZE;
private int mBaseApkAssetsSize;
@@ -364,10 +365,10 @@ public class Resources {
// Rebase the ThemeImpls using the new ResourcesImpl.
synchronized (mThemeRefs) {
+ cleanupThemeReferences();
final int count = mThemeRefs.size();
for (int i = 0; i < count; i++) {
- WeakReference<Theme> weakThemeRef = mThemeRefs.get(i);
- Theme theme = weakThemeRef != null ? weakThemeRef.get() : null;
+ Theme theme = mThemeRefs.get(i).get();
if (theme != null) {
theme.rebase(mResourcesImpl);
}
@@ -2001,6 +2002,15 @@ public class Resources {
private int mHashCode = 0;
+ private boolean containsValue(int resId, boolean force) {
+ for (int i = 0; i < mCount; ++i) {
+ if (mResId[i] == resId && mForce[i] == force) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public void append(int resId, boolean force) {
if (mResId == null) {
mResId = new int[4];
@@ -2010,6 +2020,11 @@ public class Resources {
mForce = new boolean[4];
}
+ // Some apps tend to keep adding same resources over and over, let's protect from it.
+ if (containsValue(resId, force)) {
+ return;
+ }
+
mResId = GrowingArrayUtils.append(mResId, mCount, resId);
mForce = GrowingArrayUtils.append(mForce, mCount, force);
mCount++;
@@ -2073,6 +2088,19 @@ public class Resources {
}
}
+ static int nextPowerOf2(int number) {
+ return number < 2 ? 2 : 1 >> ((int) (Math.log(number - 1) / Math.log(2)) + 1);
+ }
+
+ private void cleanupThemeReferences() {
+ // Clean up references to garbage collected themes
+ if (mThemeRefs.size() > mThemeRefsNextFlushSize) {
+ mThemeRefs.removeIf(ref -> ref.refersTo(null));
+ mThemeRefsNextFlushSize = Math.min(Math.max(MIN_THEME_REFS_FLUSH_SIZE,
+ nextPowerOf2(mThemeRefs.size())), MAX_THEME_REFS_FLUSH_SIZE);
+ }
+ }
+
/**
* Generate a new Theme object for this set of Resources. It initially
* starts out empty.
@@ -2083,14 +2111,8 @@ public class Resources {
Theme theme = new Theme();
theme.setImpl(mResourcesImpl.newThemeImpl());
synchronized (mThemeRefs) {
+ cleanupThemeReferences();
mThemeRefs.add(new WeakReference<>(theme));
-
- // Clean up references to garbage collected themes
- if (mThemeRefs.size() > mThemeRefsNextFlushSize) {
- mThemeRefs.removeIf(ref -> ref.refersTo(null));
- mThemeRefsNextFlushSize = Math.max(MIN_THEME_REFS_FLUSH_SIZE,
- 2 * mThemeRefs.size());
- }
}
return theme;
}
diff --git a/core/java/android/credentials/ui/Entry.java b/core/java/android/credentials/ui/Entry.java
new file mode 100644
index 000000000000..122c54ad8144
--- /dev/null
+++ b/core/java/android/credentials/ui/Entry.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2022 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.credentials.ui;
+
+import android.annotation.NonNull;
+import android.app.slice.Slice;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+
+/**
+ * A credential, save, or action entry to be rendered.
+ *
+ * @hide
+ */
+public class Entry implements Parcelable {
+ // TODO: move to jetpack.
+ public static final String VERSION = "v1";
+ public static final Uri CREDENTIAL_MANAGER_ENTRY_URI = Uri.parse("credentialmanager.slice");
+ public static final String HINT_TITLE = "hint_title";
+ public static final String HINT_SUBTITLE = "hint_subtitle";
+ public static final String HINT_ICON = "hint_icon";
+
+ /**
+ * The intent extra key for the action chip {@code Entry} list when launching the UX activities.
+ */
+ public static final String EXTRA_ENTRY_LIST_ACTION_CHIP =
+ "android.credentials.ui.extra.ENTRY_LIST_ACTION_CHIP";
+ /**
+ * The intent extra key for the credential / save {@code Entry} list when launching the UX
+ * activities.
+ */
+ public static final String EXTRA_ENTRY_LIST_CREDENTIAL =
+ "android.credentials.ui.extra.ENTRY_LIST_CREDENTIAL";
+ /**
+ * The intent extra key for the authentication action {@code Entry} when launching the UX
+ * activities.
+ */
+ public static final String EXTRA_ENTRY_AUTHENTICATION_ACTION =
+ "android.credentials.ui.extra.ENTRY_AUTHENTICATION_ACTION";
+
+ // TODO: may be changed to other type depending on the service implementation.
+ private final int mId;
+
+ @NonNull
+ private final Slice mSlice;
+
+ protected Entry(@NonNull Parcel in) {
+ int entryId = in.readInt();
+ Slice slice = Slice.CREATOR.createFromParcel(in);
+
+ mId = entryId;
+ mSlice = slice;
+ AnnotationValidations.validate(NonNull.class, null, mSlice);
+ }
+
+ public Entry(int id, @NonNull Slice slice) {
+ mId = id;
+ mSlice = slice;
+ }
+
+ /**
+ * Returns the id of this entry that's unique within the context of the CredentialManager
+ * request.
+ */
+ public int getEntryId() {
+ return mId;
+ }
+
+ /**
+ * Returns the Slice to be rendered.
+ */
+ @NonNull
+ public Slice getSlice() {
+ return mSlice;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mId);
+ mSlice.writeToParcel(dest, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<Entry> CREATOR = new Creator<Entry>() {
+ @Override
+ public Entry createFromParcel(@NonNull Parcel in) {
+ return new Entry(in);
+ }
+
+ @Override
+ public Entry[] newArray(int size) {
+ return new Entry[size];
+ }
+ };
+}
diff --git a/core/java/android/credentials/ui/ProviderData.java b/core/java/android/credentials/ui/ProviderData.java
index 49e5e49c2450..18e6ba430589 100644
--- a/core/java/android/credentials/ui/ProviderData.java
+++ b/core/java/android/credentials/ui/ProviderData.java
@@ -17,11 +17,15 @@
package android.credentials.ui;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.AnnotationValidations;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Holds metadata and credential entries for a single provider.
*
@@ -36,13 +40,24 @@ public class ProviderData implements Parcelable {
public static final String EXTRA_PROVIDER_DATA_LIST =
"android.credentials.ui.extra.PROVIDER_DATA_LIST";
- // TODO: add entry data.
-
@NonNull
private final String mPackageName;
+ @NonNull
+ private final List<Entry> mCredentialEntries;
+ @NonNull
+ private final List<Entry> mActionChips;
+ @Nullable
+ private final Entry mAuthenticationEntry;
- public ProviderData(@NonNull String packageName) {
+ public ProviderData(
+ @NonNull String packageName,
+ @NonNull List<Entry> credentialEntries,
+ @NonNull List<Entry> actionChips,
+ @Nullable Entry authenticationEntry) {
mPackageName = packageName;
+ mCredentialEntries = credentialEntries;
+ mActionChips = actionChips;
+ mAuthenticationEntry = authenticationEntry;
}
/** Returns the provider package name. */
@@ -51,15 +66,46 @@ public class ProviderData implements Parcelable {
return mPackageName;
}
+ @NonNull
+ public List<Entry> getCredentialEntries() {
+ return mCredentialEntries;
+ }
+
+ @NonNull
+ public List<Entry> getActionChips() {
+ return mActionChips;
+ }
+
+ @Nullable
+ public Entry getAuthenticationEntry() {
+ return mAuthenticationEntry;
+ }
+
protected ProviderData(@NonNull Parcel in) {
String packageName = in.readString8();
mPackageName = packageName;
AnnotationValidations.validate(NonNull.class, null, mPackageName);
+
+ List<Entry> credentialEntries = new ArrayList<>();
+ in.readTypedList(credentialEntries, Entry.CREATOR);
+ mCredentialEntries = credentialEntries;
+ AnnotationValidations.validate(NonNull.class, null, mCredentialEntries);
+
+ List<Entry> actionChips = new ArrayList<>();
+ in.readTypedList(actionChips, Entry.CREATOR);
+ mActionChips = actionChips;
+ AnnotationValidations.validate(NonNull.class, null, mActionChips);
+
+ Entry authenticationEntry = in.readTypedObject(Entry.CREATOR);
+ mAuthenticationEntry = authenticationEntry;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString8(mPackageName);
+ dest.writeTypedList(mCredentialEntries);
+ dest.writeTypedList(mActionChips);
+ dest.writeTypedObject(mAuthenticationEntry, flags);
}
@Override
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index 830584314039..bc6368639baa 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -299,7 +299,7 @@ public final class SQLiteDatabaseConfiguration {
if (isLegacyCompatibilityWalEnabled()) {
return SQLiteCompatibilityWalFlags.getWALSyncMode();
} else {
- return SQLiteGlobal.getDefaultSyncMode();
+ return SQLiteGlobal.getWALSyncMode();
}
} else {
return SQLiteGlobal.getDefaultSyncMode();
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index a87b1338339a..d23fb363df1c 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -17,6 +17,7 @@
package android.inputmethodservice;
import android.annotation.BinderThread;
+import android.annotation.DurationMillisLong;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -82,6 +83,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_FINISH_STYLUS_HANDWRITING = 130;
private static final int DO_UPDATE_TOOL_TYPE = 140;
private static final int DO_REMOVE_STYLUS_HANDWRITING_WINDOW = 150;
+ private static final int DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT = 160;
final WeakReference<InputMethodServiceInternal> mTarget;
final Context mContext;
@@ -151,7 +153,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
final InputMethodServiceInternal target = mTarget.get();
switch (msg.what) {
case DO_DUMP: {
- SomeArgs args = (SomeArgs)msg.obj;
+ SomeArgs args = (SomeArgs) msg.obj;
if (isValid(inputMethod, target, "DO_DUMP")) {
final FileDescriptor fd = (FileDescriptor) args.arg1;
final PrintWriter fout = (PrintWriter) args.arg2;
@@ -201,7 +203,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
return;
case DO_CREATE_SESSION: {
- SomeArgs args = (SomeArgs)msg.obj;
+ SomeArgs args = (SomeArgs) msg.obj;
if (isValid(inputMethod, target, "DO_CREATE_SESSION")) {
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
mContext, (InputChannel) args.arg1,
@@ -216,7 +218,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
return;
case DO_SHOW_SOFT_INPUT: {
- final SomeArgs args = (SomeArgs)msg.obj;
+ final SomeArgs args = (SomeArgs) msg.obj;
if (isValid(inputMethod, target, "DO_SHOW_SOFT_INPUT")) {
inputMethod.showSoftInputWithToken(
msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1);
@@ -287,6 +289,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
return;
}
+ case DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT: {
+ inputMethod.setStylusWindowIdleTimeoutForTest((long) msg.obj);
+ return;
+ }
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -300,7 +306,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
if (target.getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
-
+
fout.println("Permission Denial: can't dump InputMethodManager from from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
@@ -473,6 +479,13 @@ class IInputMethodWrapper extends IInputMethod.Stub
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_STYLUS_HANDWRITING_WINDOW));
}
+ @BinderThread
+ @Override
+ public void setStylusWindowIdleTimeoutForTest(@DurationMillisLong long timeout) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT, timeout));
+ }
+
private static boolean isValid(InputMethod inputMethod, InputMethodServiceInternal target,
String msg) {
if (inputMethod != null && target != null && !target.isServiceDestroyed()) {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 85a8551cb23f..92c3311fcc27 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -57,6 +57,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.AnyThread;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
+import android.annotation.DurationMillisLong;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -364,6 +365,11 @@ public class InputMethodService extends AbstractInputMethodService {
STYLUS_HANDWRITING_IDLE_TIMEOUT_MS * 3;
/**
+ * Stylus idle-timeout after which stylus {@code InkWindow} will be removed.
+ */
+ private static final long STYLUS_WINDOW_IDLE_TIMEOUT_MILLIS = 5 * 60 * 1000; // 5 minutes.
+
+ /**
* A circular buffer of size MAX_EVENTS_BUFFER in case IME is taking too long to add ink view.
**/
private RingBuffer<MotionEvent> mPendingEvents;
@@ -373,6 +379,8 @@ public class InputMethodService extends AbstractInputMethodService {
private Runnable mImeSurfaceRemoverRunnable;
private Runnable mFinishHwRunnable;
private long mStylusHwSessionsTimeout = STYLUS_HANDWRITING_IDLE_TIMEOUT_MS;
+ private Runnable mStylusWindowIdleTimeoutRunnable;
+ private long mStylusWindowIdleTimeoutForTest;
/**
* Returns whether {@link InputMethodService} is responsible for rendering the back button and
@@ -1050,7 +1058,6 @@ public class InputMethodService extends AbstractInputMethodService {
mInkWindow = new InkWindow(mWindow.getContext());
mInkWindow.setToken(mToken);
}
- // TODO(b/243571274): set an idle-timeout after which InkWindow is removed.
mInkWindow.initOnly();
}
@@ -1074,6 +1081,15 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public void setStylusWindowIdleTimeoutForTest(@DurationMillisLong long timeout) {
+ mStylusWindowIdleTimeoutForTest = timeout;
+ }
+
+ /**
+ * {@inheritDoc}
*/
@MainThread
@Override
@@ -2508,6 +2524,11 @@ public class InputMethodService extends AbstractInputMethodService {
});
}
}
+
+ // Create a stylus window idle-timeout after which InkWindow is removed.
+ if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
+ scheduleStylusWindowIdleTimeout();
+ }
}
/**
@@ -2568,7 +2589,6 @@ public class InputMethodService extends AbstractInputMethodService {
mHandwritingEventReceiver.dispose();
mHandwritingEventReceiver = null;
- // TODO(b/243571274): set an idle-timeout after which InkWindow is removed.
mInkWindow.hide(false /* remove */);
mPrivOps.resetStylusHandwriting(requestId);
@@ -2592,9 +2612,41 @@ public class InputMethodService extends AbstractInputMethodService {
}
private void removeHandwritingInkWindow() {
- mInkWindow.hide(true /* remove */);
- mInkWindow.destroy();
- mInkWindow = null;
+ cancelStylusWindowIdleTimeout();
+ mOnPreparedStylusHwCalled = false;
+ mStylusWindowIdleTimeoutRunnable = null;
+ if (mInkWindow != null) {
+ mInkWindow.hide(true /* remove */);
+ mInkWindow.destroy();
+ mInkWindow = null;
+ }
+ }
+
+ private void cancelStylusWindowIdleTimeout() {
+ if (mStylusWindowIdleTimeoutRunnable != null && mHandler != null) {
+ mHandler.removeCallbacks(mStylusWindowIdleTimeoutRunnable);
+ }
+ }
+
+ private void scheduleStylusWindowIdleTimeout() {
+ if (mHandler == null) {
+ return;
+ }
+ cancelStylusWindowIdleTimeout();
+ long timeout = (mStylusWindowIdleTimeoutForTest > 0)
+ ? mStylusWindowIdleTimeoutForTest : STYLUS_WINDOW_IDLE_TIMEOUT_MILLIS;
+ mHandler.postDelayed(getStylusWindowIdleTimeoutRunnable(), timeout);
+ }
+
+ private Runnable getStylusWindowIdleTimeoutRunnable() {
+ if (mStylusWindowIdleTimeoutRunnable == null) {
+ mStylusWindowIdleTimeoutRunnable = () -> {
+ removeHandwritingInkWindow();
+ mStylusWindowIdleTimeoutRunnable = null;
+ };
+ }
+
+ return mStylusWindowIdleTimeoutRunnable;
}
/**
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index adeb722833fa..7a55a5c4ef72 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2017,7 +2017,7 @@ public abstract class BatteryStats {
public static final int EVENT_PACKAGE_INSTALLED = 0x000b;
// Event for a package being uninstalled.
public static final int EVENT_PACKAGE_UNINSTALLED = 0x000c;
- // Event for a package being uninstalled.
+ // Event for an alarm being sent out to an app.
public static final int EVENT_ALARM = 0x000d;
// Record that we have decided we need to collect new stats data.
public static final int EVENT_COLLECT_EXTERNAL_STATS = 0x000e;
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 2c0be870836a..3bf9ca044141 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -115,6 +115,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
private final int mMaxStreamVolume;
private boolean mAffectedByRingerMode;
private boolean mNotificationOrRing;
+ private final boolean mNotifAliasRing;
private final Receiver mReceiver = new Receiver();
private Handler mHandler;
@@ -179,6 +180,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
if (mNotificationOrRing) {
mRingerMode = mAudioManager.getRingerModeInternal();
}
+ mNotifAliasRing = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_alias_ring_notif_stream_types);
mZenMode = mNotificationManager.getZenMode();
if (hasAudioProductStrategies()) {
@@ -280,7 +283,15 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
if (zenMuted) {
mSeekBar.setProgress(mLastAudibleStreamVolume, true);
} else if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
- mSeekBar.setProgress(0, true);
+ /**
+ * the first variable above is preserved and the conditions below are made explicit
+ * so that when user attempts to slide the notification seekbar out of vibrate the
+ * seekbar doesn't wrongly snap back to 0 when the streams aren't aliased
+ */
+ if (mNotifAliasRing || mStreamType == AudioManager.STREAM_RING
+ || (mStreamType == AudioManager.STREAM_NOTIFICATION && mMuted)) {
+ mSeekBar.setProgress(0, true);
+ }
} else if (mMuted) {
mSeekBar.setProgress(0, true);
} else {
@@ -354,6 +365,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
// set the time of stop volume
if ((mStreamType == AudioManager.STREAM_VOICE_CALL
|| mStreamType == AudioManager.STREAM_RING
+ || (!mNotifAliasRing && mStreamType == AudioManager.STREAM_NOTIFICATION)
|| mStreamType == AudioManager.STREAM_ALARM)) {
sStopVolumeTime = java.lang.System.currentTimeMillis();
}
@@ -632,8 +644,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
}
private void updateVolumeSlider(int streamType, int streamValue) {
- final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType)
- : (streamType == mStreamType);
+ final boolean streamMatch = mNotifAliasRing && mNotificationOrRing
+ ? isNotificationOrRing(streamType) : streamType == mStreamType;
if (mSeekBar != null && streamMatch && streamValue != -1) {
final boolean muted = mAudioManager.isStreamMute(mStreamType)
|| streamValue == 0;
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 194e1b553322..979c5a885c5e 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -53,6 +53,7 @@ import com.android.internal.telephony.SmsApplication;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -4358,6 +4359,12 @@ public final class Telephony {
*/
public static final String COLUMN_NAME_SOURCE = "name_source";
+ /**
+ * The name source is unknown.
+ * @hide
+ */
+ public static final int NAME_SOURCE_UNKNOWN = -1;
+
/** The name_source is from the carrier id. {@hide} */
public static final int NAME_SOURCE_CARRIER_ID = 0;
@@ -4839,5 +4846,86 @@ public final class Telephony {
* @hide
*/
public static final String COLUMN_USER_HANDLE = "user_handle";
+
+ /** All columns in {@link SimInfo} table. */
+ private static final List<String> ALL_COLUMNS = List.of(
+ COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
+ COLUMN_ICC_ID,
+ COLUMN_SIM_SLOT_INDEX,
+ COLUMN_DISPLAY_NAME,
+ COLUMN_CARRIER_NAME,
+ COLUMN_NAME_SOURCE,
+ COLUMN_COLOR,
+ COLUMN_NUMBER,
+ COLUMN_DISPLAY_NUMBER_FORMAT,
+ COLUMN_DATA_ROAMING,
+ COLUMN_MCC,
+ COLUMN_MNC,
+ COLUMN_MCC_STRING,
+ COLUMN_MNC_STRING,
+ COLUMN_EHPLMNS,
+ COLUMN_HPLMNS,
+ COLUMN_SIM_PROVISIONING_STATUS,
+ COLUMN_IS_EMBEDDED,
+ COLUMN_CARD_ID,
+ COLUMN_ACCESS_RULES,
+ COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS,
+ COLUMN_IS_REMOVABLE,
+ COLUMN_CB_EXTREME_THREAT_ALERT,
+ COLUMN_CB_SEVERE_THREAT_ALERT,
+ COLUMN_CB_AMBER_ALERT,
+ COLUMN_CB_EMERGENCY_ALERT,
+ COLUMN_CB_ALERT_SOUND_DURATION,
+ COLUMN_CB_ALERT_REMINDER_INTERVAL,
+ COLUMN_CB_ALERT_VIBRATE,
+ COLUMN_CB_ALERT_SPEECH,
+ COLUMN_CB_ETWS_TEST_ALERT,
+ COLUMN_CB_CHANNEL_50_ALERT,
+ COLUMN_CB_CMAS_TEST_ALERT,
+ COLUMN_CB_OPT_OUT_DIALOG,
+ COLUMN_ENHANCED_4G_MODE_ENABLED,
+ COLUMN_VT_IMS_ENABLED,
+ COLUMN_WFC_IMS_ENABLED,
+ COLUMN_WFC_IMS_MODE,
+ COLUMN_WFC_IMS_ROAMING_MODE,
+ COLUMN_WFC_IMS_ROAMING_ENABLED,
+ COLUMN_IS_OPPORTUNISTIC,
+ COLUMN_GROUP_UUID,
+ COLUMN_IS_METERED,
+ COLUMN_ISO_COUNTRY_CODE,
+ COLUMN_CARRIER_ID,
+ COLUMN_PROFILE_CLASS,
+ COLUMN_SUBSCRIPTION_TYPE,
+ COLUMN_GROUP_OWNER,
+ COLUMN_DATA_ENABLED_OVERRIDE_RULES,
+ COLUMN_ENABLED_MOBILE_DATA_POLICIES,
+ COLUMN_IMSI,
+ COLUMN_UICC_APPLICATIONS_ENABLED,
+ COLUMN_ALLOWED_NETWORK_TYPES,
+ COLUMN_IMS_RCS_UCE_ENABLED,
+ COLUMN_CROSS_SIM_CALLING_ENABLED,
+ COLUMN_RCS_CONFIG,
+ COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS,
+ COLUMN_D2D_STATUS_SHARING,
+ COLUMN_VOIMS_OPT_IN_STATUS,
+ COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+ COLUMN_NR_ADVANCED_CALLING_ENABLED,
+ COLUMN_PHONE_NUMBER_SOURCE_CARRIER,
+ COLUMN_PHONE_NUMBER_SOURCE_IMS,
+ COLUMN_PORT_INDEX,
+ COLUMN_USAGE_SETTING,
+ COLUMN_TP_MESSAGE_REF,
+ COLUMN_USER_HANDLE
+ );
+
+ /**
+ * @return All columns in {@link SimInfo} table.
+ *
+ * @hide
+ */
+ @NonNull
+ public static List<String> getAllColumns() {
+ return ALL_COLUMNS;
+ }
}
}
diff --git a/core/java/android/service/credentials/Action.java b/core/java/android/service/credentials/Action.java
index 186b2a60c430..e2c11fbac008 100644
--- a/core/java/android/service/credentials/Action.java
+++ b/core/java/android/service/credentials/Action.java
@@ -16,13 +16,12 @@
package android.service.credentials;
+import android.annotation.NonNull;
import android.app.PendingIntent;
import android.app.slice.Slice;
import android.os.Parcel;
import android.os.Parcelable;
-import androidx.annotation.NonNull;
-
import java.util.Objects;
/**
@@ -32,29 +31,26 @@ import java.util.Objects;
* @hide
*/
public final class Action implements Parcelable {
- /** Info to be displayed with this action on the UI. */
- private final @NonNull Slice mInfo;
- /**
- * The pending intent to be invoked when the user selects this action.
- */
+ /** Slice object containing display content to be displayed with this action on the UI. */
+ private final @NonNull Slice mSlice;
+ /** The pending intent to be invoked when the user selects this action. */
private final @NonNull PendingIntent mPendingIntent;
/**
* Constructs an action to be displayed on the UI.
*
- * @param actionInfo The info to be displayed along with this action.
- * @param pendingIntent The intent to be invoked when the user selects this action.
- * @throws NullPointerException If {@code actionInfo}, or {@code pendingIntent} is null.
+ * @param slice the display content to be displayed on the UI, along with this action
+ * @param pendingIntent the intent to be invoked when the user selects this action
*/
- public Action(@NonNull Slice actionInfo, @NonNull PendingIntent pendingIntent) {
- Objects.requireNonNull(actionInfo, "actionInfo must not be null");
+ public Action(@NonNull Slice slice, @NonNull PendingIntent pendingIntent) {
+ Objects.requireNonNull(slice, "slice must not be null");
Objects.requireNonNull(pendingIntent, "pendingIntent must not be null");
- mInfo = actionInfo;
+ mSlice = slice;
mPendingIntent = pendingIntent;
}
private Action(@NonNull Parcel in) {
- mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+ mSlice = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
PendingIntent.class);
}
@@ -78,15 +74,15 @@ public final class Action implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- mInfo.writeToParcel(dest, flags);
+ mSlice.writeToParcel(dest, flags);
mPendingIntent.writeToParcel(dest, flags);
}
/**
- * Returns the action info as a {@link Slice} object, to be displayed on the UI.
+ * Returns a {@code Slice} object containing the display content to be displayed on the UI.
*/
- public @NonNull Slice getActionInfo() {
- return mInfo;
+ public @NonNull Slice getSlice() {
+ return mSlice;
}
/**
diff --git a/core/java/android/service/credentials/CreateCredentialCallback.java b/core/java/android/service/credentials/CreateCredentialCallback.java
deleted file mode 100644
index 6108eea5bea1..000000000000
--- a/core/java/android/service/credentials/CreateCredentialCallback.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2022 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.credentials;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * Callback to be invoked as a response to {@link CreateCredentialRequest}.
- *
- * @hide
- */
-public final class CreateCredentialCallback {
- private static final String TAG = "CreateCredentialCallback";
-
- private final ICreateCredentialCallback mCallback;
-
- /** @hide */
- public CreateCredentialCallback(@NonNull ICreateCredentialCallback callback) {
- mCallback = callback;
- }
-
- /**
- * Invoked on a successful response for {@link CreateCredentialRequest}
- * @param response The response from the credential provider.
- */
- public void onSuccess(@NonNull CreateCredentialResponse response) {
- try {
- mCallback.onSuccess(response);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- }
-
- /**
- * Invoked on a failure response for {@link CreateCredentialRequest}
- * @param errorCode The code defining the type of error.
- * @param message The message corresponding to the failure.
- */
- public void onFailure(int errorCode, @Nullable CharSequence message) {
- Log.w(TAG, "onFailure: " + message);
- try {
- mCallback.onFailure(errorCode, message);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- }
-}
diff --git a/core/java/android/service/credentials/CreateCredentialRequest.java b/core/java/android/service/credentials/CreateCredentialRequest.java
index ac11e04bcb77..6a0bbc0bd917 100644
--- a/core/java/android/service/credentials/CreateCredentialRequest.java
+++ b/core/java/android/service/credentials/CreateCredentialRequest.java
@@ -16,12 +16,11 @@
package android.service.credentials;
+import android.annotation.NonNull;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import androidx.annotation.NonNull;
-
import com.android.internal.util.Preconditions;
import java.util.Objects;
diff --git a/core/java/android/service/credentials/CreateCredentialResponse.java b/core/java/android/service/credentials/CreateCredentialResponse.java
index f2ad7272f207..613eba8c9bb2 100644
--- a/core/java/android/service/credentials/CreateCredentialResponse.java
+++ b/core/java/android/service/credentials/CreateCredentialResponse.java
@@ -16,12 +16,11 @@
package android.service.credentials;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
-import androidx.annotation.NonNull;
-
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
diff --git a/core/java/android/service/credentials/Credential.java b/core/java/android/service/credentials/Credential.java
deleted file mode 100644
index 7d5da8a7c4e0..000000000000
--- a/core/java/android/service/credentials/Credential.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2022 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.credentials;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import androidx.annotation.NonNull;
-
-import static java.util.Objects.requireNonNull;
-
-import com.android.internal.util.Preconditions;
-
-/**
- * A Credential object that contains type specific data that is returned from the credential
- * provider to the framework. Framework then converts it to an app facing representation and
- * returns to the calling app.
- *
- * @hide
- */
-public final class Credential implements Parcelable {
- /** The type of this credential. */
- private final @NonNull String mType;
-
- /** The data associated with this credential. */
- private final @NonNull Bundle mData;
-
- /**
- * Constructs a credential object.
- *
- * @param type The type of the credential.
- * @param data The data of the credential that is passed back to the framework, and eventually
- * to the calling app.
- * @throws NullPointerException If {@code data} is null.
- * @throws IllegalArgumentException If {@code type} is null or empty.
- */
- public Credential(@NonNull String type, @NonNull Bundle data) {
- Preconditions.checkStringNotEmpty(type, "type must not be null, or empty");
- requireNonNull(data, "data must not be null");
- this.mType = type;
- this.mData = data;
- }
-
- private Credential(@NonNull Parcel in) {
- mType = in.readString16NoHelper();
- mData = in.readBundle();
- }
-
- /**
- * Returns the type of the credential.
- */
- public @NonNull String getType() {
- return mType;
- }
-
- /**
- * Returns the data associated with the credential.
- */
- public @NonNull Bundle getData() {
- return mData;
- }
-
- public static final @NonNull Creator<Credential> CREATOR = new Creator<Credential>() {
- @Override
- public Credential createFromParcel(@NonNull Parcel in) {
- return new Credential(in);
- }
-
- @Override
- public Credential[] newArray(int size) {
- return new Credential[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString8(mType);
- dest.writeBundle(mData);
- }
-}
diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java
index b49215a08bfb..49b84359d94a 100644
--- a/core/java/android/service/credentials/CredentialEntry.java
+++ b/core/java/android/service/credentials/CredentialEntry.java
@@ -16,14 +16,14 @@
package android.service.credentials;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.app.slice.Slice;
+import android.credentials.Credential;
import android.os.Parcel;
import android.os.Parcelable;
-import androidx.annotation.NonNull;
-
import com.android.internal.util.Preconditions;
import java.util.Objects;
@@ -38,8 +38,9 @@ public final class CredentialEntry implements Parcelable {
/** The type of the credential entry to be shown on the UI. */
private final @NonNull String mType;
- /** The info to be displayed along with this credential entry on the UI. */
- private final @NonNull Slice mInfo;
+ /** The object containing display content to be shown along with this credential entry
+ * on the UI. */
+ private final @NonNull Slice mSlice;
/** The pending intent to be invoked when this credential entry is selected. */
private final @Nullable PendingIntent mPendingIntent;
@@ -53,11 +54,11 @@ public final class CredentialEntry implements Parcelable {
/** A flag denoting whether auto-select is enabled for this entry. */
private final @NonNull boolean mAutoSelectAllowed;
- private CredentialEntry(@NonNull String type, @NonNull Slice entryInfo,
+ private CredentialEntry(@NonNull String type, @NonNull Slice slice,
@Nullable PendingIntent pendingIntent, @Nullable Credential credential,
@NonNull boolean autoSeletAllowed) {
mType = type;
- mInfo = entryInfo;
+ mSlice = slice;
mPendingIntent = pendingIntent;
mCredential = credential;
mAutoSelectAllowed = autoSeletAllowed;
@@ -65,7 +66,7 @@ public final class CredentialEntry implements Parcelable {
private CredentialEntry(@NonNull Parcel in) {
mType = in.readString();
- mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+ mSlice = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
PendingIntent.class);
mCredential = in.readParcelable(Credential.class.getClassLoader(),
@@ -94,7 +95,7 @@ public final class CredentialEntry implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString8(mType);
- mInfo.writeToParcel(dest, flags);
+ mSlice.writeToParcel(dest, flags);
mPendingIntent.writeToParcel(dest, flags);
mCredential.writeToParcel(dest, flags);
dest.writeBoolean(mAutoSelectAllowed);
@@ -108,10 +109,10 @@ public final class CredentialEntry implements Parcelable {
}
/**
- * Returns the UI info to be displayed for this entry.
+ * Returns the {@link Slice} object containing UI display content to be shown for this entry.
*/
- public @NonNull Slice getInfo() {
- return mInfo;
+ public @NonNull Slice getSlice() {
+ return mSlice;
}
/**
@@ -131,7 +132,7 @@ public final class CredentialEntry implements Parcelable {
/**
* Returns whether this entry can be auto selected if it is the only option for the user.
*/
- public @NonNull boolean isAutoSelectAllowed() {
+ public boolean isAutoSelectAllowed() {
return mAutoSelectAllowed;
}
@@ -140,28 +141,35 @@ public final class CredentialEntry implements Parcelable {
*/
public static final class Builder {
private String mType;
- private Slice mInfo;
+ private Slice mSlice;
private PendingIntent mPendingIntent;
private Credential mCredential;
private boolean mAutoSelectAllowed = false;
/**
* Builds the instance.
- * @param type The type of credential underlying this credential entry.
- * @param info The info to be displayed with this entry on the UI.
+ * @param type the type of credential underlying this credential entry
+ * @param slice the content to be displayed with this entry on the UI
*
* @throws IllegalArgumentException If {@code type} is null or empty.
- * @throws NullPointerException If {@code info} is null.
+ * @throws NullPointerException If {@code slice} is null.
*/
- public Builder(@NonNull String type, @NonNull Slice info) {
+ public Builder(@NonNull String type, @NonNull Slice slice) {
mType = Preconditions.checkStringNotEmpty(type, "type must not be "
+ "null, or empty");
- mInfo = Objects.requireNonNull(info, "info must not be null");
+ mSlice = Objects.requireNonNull(slice,
+ "slice must not be null");
}
/**
* Sets the pendingIntent to be invoked if the user selects this entry.
*
+ * The pending intent can be used to launch activities that require some user engagement
+ * before getting the credential corresponding to this entry, e.g. authentication,
+ * confirmation etc.
+ * Once the activity fulfills the required user engagement, a {@link Credential} object
+ * must be returned as an extra on activity finish.
+ *
* @throws IllegalStateException If {@code credential} is already set. Must either set the
* {@code credential}, or the {@code pendingIntent}.
*/
@@ -199,7 +207,7 @@ public final class CredentialEntry implements Parcelable {
/**
* Creates a new {@link CredentialEntry} instance.
*
- * @throws NullPointerException If {@code info} is null.
+ * @throws NullPointerException If {@code slice} is null.
* @throws IllegalArgumentException If {@code type} is null, or empty.
* @throws IllegalStateException If neither {@code pendingIntent} nor {@code credential}
* is set, or if both are set.
@@ -209,7 +217,7 @@ public final class CredentialEntry implements Parcelable {
"Either pendingIntent or credential must be set");
Preconditions.checkState(mPendingIntent != null && mCredential != null,
"Cannot set both the pendingIntent and credential");
- return new CredentialEntry(mType, mInfo, mPendingIntent,
+ return new CredentialEntry(mType, mSlice, mPendingIntent,
mCredential, mAutoSelectAllowed);
}
}
diff --git a/core/java/android/service/credentials/CredentialProviderException.java b/core/java/android/service/credentials/CredentialProviderException.java
new file mode 100644
index 000000000000..b39b4a0cc180
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialProviderException.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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.credentials;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains custom exceptions to be used by credential providers on failure.
+ *
+ * @hide
+ */
+public class CredentialProviderException extends Exception {
+ public static final int ERROR_UNKNOWN = 0;
+
+ private final int mErrorCode;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"ERROR_"}, value = {
+ ERROR_UNKNOWN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CredentialProviderError { }
+
+
+ public CredentialProviderException(@CredentialProviderError int errorCode,
+ @NonNull String message) {
+ super(message);
+ mErrorCode = errorCode;
+ }
+
+ public CredentialProviderException(@CredentialProviderError int errorCode,
+ @NonNull Throwable cause) {
+ super(cause);
+ mErrorCode = errorCode;
+ }
+
+ public CredentialProviderException(@CredentialProviderError int errorCode) {
+ super();
+ mErrorCode = errorCode;
+ }
+
+ public @CredentialProviderError int getErrorCode() {
+ return mErrorCode;
+ }
+}
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
index 1fe89dffaa0f..1cdf186d898c 100644
--- a/core/java/android/service/credentials/CredentialProviderService.java
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa
import android.annotation.CallSuper;
import android.annotation.NonNull;
+import android.annotation.SdkConstant;
import android.app.Service;
import android.content.Intent;
import android.os.CancellationSignal;
@@ -27,13 +28,14 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.Looper;
+import android.os.OutcomeReceiver;
import android.os.RemoteException;
import android.util.Log;
import java.util.Objects;
/**
- * Main service to be extended by credential providers, in order to return user credentials
+ * Service to be extended by credential providers, in order to return user credentials
* to the framework.
*
* @hide
@@ -42,6 +44,12 @@ public abstract class CredentialProviderService extends Service {
private static final String TAG = "CredProviderService";
private Handler mHandler;
+ /**
+ * The {@link Intent} that must be declared as handled by the service. The service must also
+ * require the {android.Manifest.permission#BIND_CREDENTIAL_PROVIDER_SERVICE} permission
+ * so that only the system can bind to it.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE =
"android.service.credentials.CredentialProviderService";
@@ -64,7 +72,7 @@ public abstract class CredentialProviderService extends Service {
private final ICredentialProviderService mInterface = new ICredentialProviderService.Stub() {
@Override
public void onGetCredentials(GetCredentialsRequest request, ICancellationSignal transport,
- IGetCredentialsCallback callback) throws RemoteException {
+ IGetCredentialsCallback callback) {
Objects.requireNonNull(request);
Objects.requireNonNull(transport);
Objects.requireNonNull(callback);
@@ -73,14 +81,30 @@ public abstract class CredentialProviderService extends Service {
CredentialProviderService::onGetCredentials,
CredentialProviderService.this, request,
CancellationSignal.fromTransport(transport),
- new GetCredentialsCallback(callback)
+ new OutcomeReceiver<GetCredentialsResponse, CredentialProviderException>() {
+ @Override
+ public void onResult(GetCredentialsResponse result) {
+ try {
+ callback.onSuccess(result);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ @Override
+ public void onError(CredentialProviderException e) {
+ try {
+ callback.onFailure(e.getErrorCode(), e.getMessage());
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+ }
+ }
));
}
@Override
public void onCreateCredential(CreateCredentialRequest request,
- ICancellationSignal transport, ICreateCredentialCallback callback)
- throws RemoteException {
+ ICancellationSignal transport, ICreateCredentialCallback callback) {
Objects.requireNonNull(request);
Objects.requireNonNull(transport);
Objects.requireNonNull(callback);
@@ -89,7 +113,24 @@ public abstract class CredentialProviderService extends Service {
CredentialProviderService::onCreateCredential,
CredentialProviderService.this, request,
CancellationSignal.fromTransport(transport),
- new CreateCredentialCallback(callback)
+ new OutcomeReceiver<CreateCredentialResponse, CredentialProviderException>() {
+ @Override
+ public void onResult(CreateCredentialResponse result) {
+ try {
+ callback.onSuccess(result);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ @Override
+ public void onError(CredentialProviderException e) {
+ try {
+ callback.onFailure(e.getErrorCode(), e.getMessage());
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+ }
+ }
));
}
};
@@ -97,14 +138,14 @@ public abstract class CredentialProviderService extends Service {
/**
* Called by the android system to retrieve user credentials from the connected provider
* service.
- * @param request The credential request for the provider to handle.
- * @param cancellationSignal Signal for providers to listen to any cancellation requests from
- * the android system.
- * @param callback Object used to relay the response of the credentials request.
+ * @param request the credential request for the provider to handle
+ * @param cancellationSignal signal for providers to listen to any cancellation requests from
+ * the android system
+ * @param callback object used to relay the response of the credentials request
*/
public abstract void onGetCredentials(@NonNull GetCredentialsRequest request,
@NonNull CancellationSignal cancellationSignal,
- @NonNull GetCredentialsCallback callback);
+ @NonNull OutcomeReceiver<GetCredentialsResponse, CredentialProviderException> callback);
/**
* Called by the android system to create a credential.
@@ -115,5 +156,6 @@ public abstract class CredentialProviderService extends Service {
*/
public abstract void onCreateCredential(@NonNull CreateCredentialRequest request,
@NonNull CancellationSignal cancellationSignal,
- @NonNull CreateCredentialCallback callback);
+ @NonNull OutcomeReceiver<CreateCredentialResponse,
+ CredentialProviderException> callback);
}
diff --git a/core/java/android/service/credentials/CredentialsDisplayContent.java b/core/java/android/service/credentials/CredentialsDisplayContent.java
index 106f322c4ab4..4133ea5955c4 100644
--- a/core/java/android/service/credentials/CredentialsDisplayContent.java
+++ b/core/java/android/service/credentials/CredentialsDisplayContent.java
@@ -16,12 +16,11 @@
package android.service.credentials;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
-import androidx.annotation.NonNull;
-
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
diff --git a/core/java/android/service/credentials/GetCredentialOption.java b/core/java/android/service/credentials/GetCredentialOption.java
deleted file mode 100644
index c6cda1d1abf6..000000000000
--- a/core/java/android/service/credentials/GetCredentialOption.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2022 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.credentials;
-
-import android.annotation.NonNull;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.Preconditions;
-
-import static java.util.Objects.requireNonNull;
-
-/**
- * A type specific credential request, containing the associated data to be used for
- * retrieving credentials.
- *
- * @hide
- */
-public final class GetCredentialOption implements Parcelable {
- /** The type of credential requested. */
- private final @NonNull String mType;
-
- /** The data associated with the request. */
- private final @NonNull Bundle mData;
-
- /**
- * Constructs a new instance of {@link GetCredentialOption}
- *
- * @throws IllegalArgumentException If {@code type} string is null or empty.
- * @throws NullPointerException If {@code data} is null.
- */
- public GetCredentialOption(@NonNull String type, @NonNull Bundle data) {
- Preconditions.checkStringNotEmpty(type, "type must not be null, or empty");
- requireNonNull(data, "data must not be null");
- mType = type;
- mData = data;
- }
-
- /**
- * Returns the data associated with this credential request option.
- */
- public @NonNull Bundle getData() {
- return mData;
- }
-
- /**
- * Returns the type associated with this credential request option.
- */
- public @NonNull String getType() {
- return mType;
- }
-
- private GetCredentialOption(@NonNull Parcel in) {
- mType = in.readString16NoHelper();
- mData = in.readBundle();
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString16NoHelper(mType);
- dest.writeBundle(mData);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public static final @NonNull Creator<GetCredentialOption> CREATOR =
- new Creator<GetCredentialOption>() {
- @Override
- public GetCredentialOption createFromParcel(@NonNull Parcel in) {
- return new GetCredentialOption(in);
- }
-
- @Override
- public GetCredentialOption[] newArray(int size) {
- return new GetCredentialOption[size];
- }
- };
-}
diff --git a/core/java/android/service/credentials/GetCredentialsCallback.java b/core/java/android/service/credentials/GetCredentialsCallback.java
deleted file mode 100644
index 42a73946b5cc..000000000000
--- a/core/java/android/service/credentials/GetCredentialsCallback.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2022 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.credentials;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * Callback to be invoked as a response to {@link GetCredentialsRequest}.
- *
- * @hide
- */
-public final class GetCredentialsCallback {
-
- private static final String TAG = "GetCredentialsCallback";
-
- private final IGetCredentialsCallback mCallback;
-
- /** @hide */
- public GetCredentialsCallback(@NonNull IGetCredentialsCallback callback) {
- mCallback = callback;
- }
-
- /**
- * Invoked on a successful response for {@link GetCredentialsRequest}
- * @param response The response from the credential provider.
- */
- public void onSuccess(@NonNull GetCredentialsResponse response) {
- try {
- mCallback.onSuccess(response);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- }
-
- /**
- * Invoked on a failure response for {@link GetCredentialsRequest}
- * @param errorCode The code defining the kind of error.
- * @param message The message corresponding to the failure.
- */
- public void onFailure(int errorCode, @Nullable CharSequence message) {
- Log.w(TAG, "onFailure: " + message);
- try {
- mCallback.onFailure(errorCode, message);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- }
-}
diff --git a/core/java/android/service/credentials/GetCredentialsRequest.java b/core/java/android/service/credentials/GetCredentialsRequest.java
index cf7c2834f75f..5b1a1713ee51 100644
--- a/core/java/android/service/credentials/GetCredentialsRequest.java
+++ b/core/java/android/service/credentials/GetCredentialsRequest.java
@@ -16,11 +16,11 @@
package android.service.credentials;
+import android.annotation.NonNull;
+import android.credentials.GetCredentialOption;
import android.os.Parcel;
import android.os.Parcelable;
-import androidx.annotation.NonNull;
-
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
@@ -100,7 +100,7 @@ public final class GetCredentialsRequest implements Parcelable {
/**
* Creates a new builder.
- * @param callingPackage The calling package of the app requesting credentials.
+ * @param callingPackage the calling package of the app requesting credentials
*
* @throws IllegalArgumentException If {@code callingPackag}e is null or empty.
*/
diff --git a/core/java/android/service/credentials/GetCredentialsResponse.java b/core/java/android/service/credentials/GetCredentialsResponse.java
index 293867ba55b2..980d9ae47aa7 100644
--- a/core/java/android/service/credentials/GetCredentialsResponse.java
+++ b/core/java/android/service/credentials/GetCredentialsResponse.java
@@ -16,12 +16,11 @@
package android.service.credentials;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
-import androidx.annotation.NonNull;
-
import java.util.Objects;
/**
@@ -44,6 +43,11 @@ public final class GetCredentialsResponse implements Parcelable {
* Creates a {@link GetCredentialsRequest} instance with an authentication action set.
* Providers must use this method when no content can be shown before authentication.
*
+ * Once the authentication action activity is launched, and the user is authenticated, providers
+ * should create another response with {@link CredentialsDisplayContent} using
+ * {@code createWithDisplayContent}, and add that response to the result of the authentication
+ * activity.
+ *
* @throws NullPointerException If {@code authenticationAction} is null.
*/
public static @NonNull GetCredentialsResponse createWithAuthentication(
@@ -104,17 +108,10 @@ public final class GetCredentialsResponse implements Parcelable {
}
/**
- * Returns whether the response contains a top level authentication action.
- */
- public @NonNull boolean isAuthenticationActionSet() {
- return mAuthenticationAction != null;
- }
-
- /**
* Returns the authentication action to be invoked before any other content
* can be shown to the user.
*/
- public @NonNull Action getAuthenticationAction() {
+ public @Nullable Action getAuthenticationAction() {
return mAuthenticationAction;
}
@@ -122,7 +119,7 @@ public final class GetCredentialsResponse implements Parcelable {
* Returns the credentialDisplayContent that does not require authentication, and
* can be shown to the user on the account selector UI.
*/
- public @NonNull CredentialsDisplayContent getCredentialsDisplayContent() {
+ public @Nullable CredentialsDisplayContent getCredentialsDisplayContent() {
return mCredentialsDisplayContent;
}
}
diff --git a/core/java/android/service/credentials/SaveEntry.java b/core/java/android/service/credentials/SaveEntry.java
index 28fec30caa89..18644f09ecef 100644
--- a/core/java/android/service/credentials/SaveEntry.java
+++ b/core/java/android/service/credentials/SaveEntry.java
@@ -16,14 +16,14 @@
package android.service.credentials;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.app.slice.Slice;
+import android.credentials.Credential;
import android.os.Parcel;
import android.os.Parcelable;
-import androidx.annotation.NonNull;
-
import com.android.internal.util.Preconditions;
import java.util.Objects;
@@ -35,12 +35,12 @@ import java.util.Objects;
* @hide
*/
public final class SaveEntry implements Parcelable {
- private final @NonNull Slice mInfo;
+ private final @NonNull Slice mSlice;
private final @Nullable PendingIntent mPendingIntent;
private final @Nullable Credential mCredential;
private SaveEntry(@NonNull Parcel in) {
- mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+ mSlice = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
PendingIntent.class);
mCredential = in.readParcelable(Credential.class.getClassLoader(), Credential.class);
@@ -65,25 +65,25 @@ public final class SaveEntry implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- mInfo.writeToParcel(dest, flags);
+ mSlice.writeToParcel(dest, flags);
mPendingIntent.writeToParcel(dest, flags);
mCredential.writeToParcel(dest, flags);
}
/* package-private */ SaveEntry(
- @NonNull Slice info,
+ @NonNull Slice slice,
@Nullable PendingIntent pendingIntent,
@Nullable Credential credential) {
- this.mInfo = info;
+ this.mSlice = slice;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mInfo);
+ NonNull.class, null, mSlice);
this.mPendingIntent = pendingIntent;
this.mCredential = credential;
}
- /** Returns the info to be displayed with this save entry on the UI. */
- public @NonNull Slice getInfo() {
- return mInfo;
+ /** Returns the content to be displayed with this save entry on the UI. */
+ public @NonNull Slice getSlice() {
+ return mSlice;
}
/** Returns the pendingIntent to be invoked when this save entry on the UI is selectcd. */
@@ -101,18 +101,18 @@ public final class SaveEntry implements Parcelable {
*/
public static final class Builder {
- private @NonNull Slice mInfo;
+ private @NonNull Slice mSlice;
private @Nullable PendingIntent mPendingIntent;
private @Nullable Credential mCredential;
/**
* Builds the instance.
- * @param info The info to be displayed with this save entry.
+ * @param slice the content to be displayed with this save entry
*
- * @throws NullPointerException If {@code info} is null.
+ * @throws NullPointerException If {@code slice} is null.
*/
- public Builder(@NonNull Slice info) {
- mInfo = Objects.requireNonNull(info, "info must not be null");
+ public Builder(@NonNull Slice slice) {
+ mSlice = Objects.requireNonNull(slice, "slice must not be null");
}
/**
@@ -154,7 +154,7 @@ public final class SaveEntry implements Parcelable {
"pendingIntent and credential both must not be null. Must set "
+ "either the pendingIntnet or the credential");
return new SaveEntry(
- mInfo,
+ mSlice,
mPendingIntent,
mCredential);
}
diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
index 6956cd4cae6b..295171ca9bbd 100644
--- a/core/java/android/service/dreams/DreamManagerInternal.java
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -29,16 +29,18 @@ public abstract class DreamManagerInternal {
*
* @param doze If true, starts the doze dream component if one has been configured,
* otherwise starts the user-specified dream.
+ * @param reason The reason to start dreaming, which is logged to help debugging.
*/
- public abstract void startDream(boolean doze);
+ public abstract void startDream(boolean doze, String reason);
/**
* Called by the power manager to stop a dream.
*
* @param immediate If true, ends the dream summarily, otherwise gives it some time
* to perform a proper exit transition.
+ * @param reason The reason to stop dreaming, which is logged to help debugging.
*/
- public abstract void stopDream(boolean immediate);
+ public abstract void stopDream(boolean immediate, String reason);
/**
* Called by the power manager to determine whether a dream is running.
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 13913268cad0..3b7698e3954b 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -31,9 +31,9 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -68,6 +68,8 @@ import android.view.accessibility.AccessibilityEvent;
import com.android.internal.R;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.ObservableServiceConnection;
+import com.android.internal.util.PersistentServiceConnection;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -75,7 +77,8 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -225,6 +228,7 @@ public class DreamService extends Service implements Window.Callback {
/**
* The default value for whether to show complications on the overlay.
+ *
* @hide
*/
public static final boolean DEFAULT_SHOW_COMPLICATIONS = false;
@@ -251,77 +255,66 @@ public class DreamService extends Service implements Window.Callback {
private DreamServiceWrapper mDreamServiceWrapper;
private Runnable mDispatchAfterOnAttachedToWindow;
- private final OverlayConnection mOverlayConnection;
+ private OverlayConnection mOverlayConnection;
- private static class OverlayConnection implements ServiceConnection {
+ private static class OverlayConnection extends PersistentServiceConnection<IDreamOverlay> {
// Overlay set during onBind.
private IDreamOverlay mOverlay;
- // A Queue of pending requests to execute on the overlay.
- private final ArrayDeque<Consumer<IDreamOverlay>> mRequests;
-
- private boolean mBound;
-
- OverlayConnection() {
- mRequests = new ArrayDeque<>();
- }
-
- public void bind(Context context, @Nullable ComponentName overlayService,
- ComponentName dreamService) {
- if (overlayService == null) {
- return;
+ // A list of pending requests to execute on the overlay.
+ private final ArrayList<Consumer<IDreamOverlay>> mConsumers = new ArrayList<>();
+
+ private final Callback<IDreamOverlay> mCallback = new Callback<IDreamOverlay>() {
+ @Override
+ public void onConnected(ObservableServiceConnection<IDreamOverlay> connection,
+ IDreamOverlay service) {
+ mOverlay = service;
+ for (Consumer<IDreamOverlay> consumer : mConsumers) {
+ consumer.accept(mOverlay);
+ }
}
- final ServiceInfo serviceInfo = fetchServiceInfo(context, dreamService);
-
- final Intent overlayIntent = new Intent();
- overlayIntent.setComponent(overlayService);
- overlayIntent.putExtra(EXTRA_SHOW_COMPLICATIONS,
- fetchShouldShowComplications(context, serviceInfo));
- overlayIntent.putExtra(EXTRA_DREAM_COMPONENT, dreamService);
-
- context.bindService(overlayIntent,
- this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE);
- mBound = true;
- }
-
- public void unbind(Context context) {
- if (!mBound) {
- return;
+ @Override
+ public void onDisconnected(ObservableServiceConnection<IDreamOverlay> connection,
+ int reason) {
+ mOverlay = null;
}
+ };
- context.unbindService(this);
- mBound = false;
+ OverlayConnection(Context context,
+ Executor executor,
+ Handler handler,
+ ServiceTransformer<IDreamOverlay> transformer,
+ Intent serviceIntent,
+ int flags,
+ int minConnectionDurationMs,
+ int maxReconnectAttempts,
+ int baseReconnectDelayMs) {
+ super(context, executor, handler, transformer, serviceIntent, flags,
+ minConnectionDurationMs,
+ maxReconnectAttempts, baseReconnectDelayMs);
}
- public void request(Consumer<IDreamOverlay> request) {
- mRequests.push(request);
- evaluate();
+ @Override
+ public boolean bind() {
+ addCallback(mCallback);
+ return super.bind();
}
- private void evaluate() {
- if (mOverlay == null) {
- return;
- }
-
- // Any new requests that arrive during this loop will be processed synchronously after
- // the loop exits.
- while (!mRequests.isEmpty()) {
- final Consumer<IDreamOverlay> request = mRequests.pop();
- request.accept(mOverlay);
- }
+ @Override
+ public void unbind() {
+ removeCallback(mCallback);
+ super.unbind();
}
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- // Store Overlay and execute pending requests.
- mOverlay = IDreamOverlay.Stub.asInterface(service);
- evaluate();
+ public void addConsumer(Consumer<IDreamOverlay> consumer) {
+ mConsumers.add(consumer);
+ if (mOverlay != null) {
+ consumer.accept(mOverlay);
+ }
}
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // Clear Overlay binder to prevent further request processing.
- mOverlay = null;
+ public void removeConsumer(Consumer<IDreamOverlay> consumer) {
+ mConsumers.remove(consumer);
}
}
@@ -336,7 +329,6 @@ public class DreamService extends Service implements Window.Callback {
public DreamService() {
mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
- mOverlayConnection = new OverlayConnection();
}
/**
@@ -532,7 +524,7 @@ public class DreamService extends Service implements Window.Callback {
return mWindow;
}
- /**
+ /**
* Inflates a layout resource and set it to be the content view for this Dream.
* Behaves similarly to {@link android.app.Activity#setContentView(int)}.
*
@@ -996,13 +988,33 @@ public class DreamService extends Service implements Window.Callback {
public final IBinder onBind(Intent intent) {
if (mDebug) Slog.v(mTag, "onBind() intent = " + intent);
mDreamServiceWrapper = new DreamServiceWrapper();
+ final ComponentName overlayComponent = intent.getParcelableExtra(
+ EXTRA_DREAM_OVERLAY_COMPONENT, ComponentName.class);
// Connect to the overlay service if present.
- if (!mWindowless) {
- mOverlayConnection.bind(
+ if (!mWindowless && overlayComponent != null) {
+ final Resources resources = getResources();
+ final ComponentName dreamService = new ComponentName(this, getClass());
+
+ final ServiceInfo serviceInfo = fetchServiceInfo(this, dreamService);
+ final Intent overlayIntent = new Intent()
+ .setComponent(overlayComponent)
+ .putExtra(EXTRA_SHOW_COMPLICATIONS,
+ fetchShouldShowComplications(this, serviceInfo))
+ .putExtra(EXTRA_DREAM_COMPONENT, dreamService);
+
+ mOverlayConnection = new OverlayConnection(
/* context= */ this,
- intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT, android.content.ComponentName.class),
- new ComponentName(this, getClass()));
+ getMainExecutor(),
+ mHandler,
+ IDreamOverlay.Stub::asInterface,
+ overlayIntent,
+ /* flags= */ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ resources.getInteger(R.integer.config_minDreamOverlayDurationMs),
+ resources.getInteger(R.integer.config_dreamOverlayMaxReconnectAttempts),
+ resources.getInteger(R.integer.config_dreamOverlayReconnectTimeoutMs));
+
+ mOverlayConnection.bind();
}
return mDreamServiceWrapper;
@@ -1011,7 +1023,9 @@ public class DreamService extends Service implements Window.Callback {
@Override
public boolean onUnbind(Intent intent) {
// We must unbind from any overlay connection if we are unbound before finishing.
- mOverlayConnection.unbind(this);
+ if (mOverlayConnection != null) {
+ mOverlayConnection.unbind();
+ }
return super.onUnbind(intent);
}
@@ -1040,7 +1054,9 @@ public class DreamService extends Service implements Window.Callback {
}
mFinished = true;
- mOverlayConnection.unbind(this);
+ if (mOverlayConnection != null) {
+ mOverlayConnection.unbind();
+ }
if (mDreamToken == null) {
Slog.w(mTag, "Finish was called before the dream was attached.");
@@ -1337,19 +1353,24 @@ public class DreamService extends Service implements Window.Callback {
mWindow.getDecorView().addOnAttachStateChangeListener(
new View.OnAttachStateChangeListener() {
+ private Consumer<IDreamOverlay> mDreamStartOverlayConsumer;
+
@Override
public void onViewAttachedToWindow(View v) {
mDispatchAfterOnAttachedToWindow.run();
- // Request the DreamOverlay be told to dream with dream's window parameters
- // once the window has been attached.
- mOverlayConnection.request(overlay -> {
- try {
- overlay.startDream(mWindow.getAttributes(), mOverlayCallback);
- } catch (RemoteException e) {
- Log.e(mTag, "could not send window attributes:" + e);
- }
- });
+ if (mOverlayConnection != null) {
+ // Request the DreamOverlay be told to dream with dream's window
+ // parameters once the window has been attached.
+ mDreamStartOverlayConsumer = overlay -> {
+ try {
+ overlay.startDream(mWindow.getAttributes(), mOverlayCallback);
+ } catch (RemoteException e) {
+ Log.e(mTag, "could not send window attributes:" + e);
+ }
+ };
+ mOverlayConnection.addConsumer(mDreamStartOverlayConsumer);
+ }
}
@Override
@@ -1362,6 +1383,9 @@ public class DreamService extends Service implements Window.Callback {
mActivity = null;
finish();
}
+ if (mOverlayConnection != null && mDreamStartOverlayConsumer != null) {
+ mOverlayConnection.removeConsumer(mDreamStartOverlayConsumer);
+ }
}
});
}
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index 206e4fa4fb11..e5ad85cb526f 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -42,7 +42,8 @@ public final class NotificationStats implements Parcelable {
/** @hide */
@IntDef(prefix = { "DISMISSAL_SURFACE_" }, value = {
- DISMISSAL_NOT_DISMISSED, DISMISSAL_OTHER, DISMISSAL_PEEK, DISMISSAL_AOD, DISMISSAL_SHADE
+ DISMISSAL_NOT_DISMISSED, DISMISSAL_OTHER, DISMISSAL_PEEK, DISMISSAL_AOD,
+ DISMISSAL_SHADE, DISMISSAL_BUBBLE, DISMISSAL_LOCKSCREEN
})
@Retention(RetentionPolicy.SOURCE)
public @interface DismissalSurface {}
@@ -75,7 +76,12 @@ public final class NotificationStats implements Parcelable {
* Notification has been dismissed as a bubble.
* @hide
*/
- public static final int DISMISSAL_BUBBLE = 3;
+ public static final int DISMISSAL_BUBBLE = 4;
+ /**
+ * Notification has been dismissed from the lock screen.
+ * @hide
+ */
+ public static final int DISMISSAL_LOCKSCREEN = 5;
/** @hide */
@IntDef(prefix = { "DISMISS_SENTIMENT_" }, value = {
diff --git a/core/java/android/service/voice/HotwordAudioStream.aidl b/core/java/android/service/voice/HotwordAudioStream.aidl
new file mode 100644
index 000000000000..9550c830aecc
--- /dev/null
+++ b/core/java/android/service/voice/HotwordAudioStream.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 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.voice;
+
+parcelable HotwordAudioStream;
diff --git a/core/java/android/service/voice/HotwordAudioStream.java b/core/java/android/service/voice/HotwordAudioStream.java
new file mode 100644
index 000000000000..18375add95a7
--- /dev/null
+++ b/core/java/android/service/voice/HotwordAudioStream.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2022 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.voice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.AudioTimestamp;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import com.android.internal.util.DataClass;
+
+import java.util.Objects;
+
+/**
+ * Represents an audio stream supporting the hotword detection.
+ *
+ * @hide
+ */
+@DataClass(
+ genConstructor = false,
+ genBuilder = true,
+ genEqualsHashCode = true,
+ genParcelable = true,
+ genToString = true
+)
+@SystemApi
+public final class HotwordAudioStream implements Parcelable {
+
+ /**
+ * The {@link AudioFormat} of the audio stream.
+ */
+ @NonNull
+ private final AudioFormat mAudioFormat;
+
+ /**
+ * This stream starts with the audio bytes used for hotword detection, but continues streaming
+ * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
+ */
+ @NonNull
+ private final ParcelFileDescriptor mAudioStream;
+
+ /**
+ * The timestamp when the {@link #getAudioStream()} was captured by the Audio platform.
+ *
+ * <p>
+ * The {@link HotwordDetectionService} egressing the audio is the owner of the underlying
+ * AudioRecord. The {@link HotwordDetectionService} is expected to optionally populate this
+ * field by {@link AudioRecord#getTimestamp}.
+ * </p>
+ *
+ * <p>
+ * This timestamp can be used in conjunction with the
+ * {@link HotwordDetectedResult#getHotwordOffsetMillis()} and
+ * {@link HotwordDetectedResult#getHotwordDurationMillis()} to translate these durations to
+ * timestamps.
+ * </p>
+ */
+ @Nullable
+ private final AudioTimestamp mTimestamp;
+ private static AudioTimestamp defaultTimestamp() {
+ return null;
+ }
+
+ /**
+ * The metadata associated with the audio stream.
+ */
+ @NonNull
+ private final PersistableBundle mMetadata;
+ private static PersistableBundle defaultMetadata() {
+ return new PersistableBundle();
+ }
+
+ private String timestampToString() {
+ if (mTimestamp == null) {
+ return "";
+ }
+ return "TimeStamp:"
+ + " framePos=" + mTimestamp.framePosition
+ + " nanoTime=" + mTimestamp.nanoTime;
+ }
+
+ private void parcelTimestamp(Parcel dest, int flags) {
+ if (mTimestamp != null) {
+ // mTimestamp is not null, we write it to the parcel, set true.
+ dest.writeBoolean(true);
+ dest.writeLong(mTimestamp.framePosition);
+ dest.writeLong(mTimestamp.nanoTime);
+ } else {
+ // mTimestamp is null, we don't write any value out, set false.
+ dest.writeBoolean(false);
+ }
+ }
+
+ @Nullable
+ private static AudioTimestamp unparcelTimestamp(Parcel in) {
+ // If it is true, it means we wrote the value to the parcel before, parse it.
+ // Otherwise, return null.
+ if (in.readBoolean()) {
+ final AudioTimestamp timeStamp = new AudioTimestamp();
+ timeStamp.framePosition = in.readLong();
+ timeStamp.nanoTime = in.readLong();
+ return timeStamp;
+ } else {
+ return null;
+ }
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/HotwordAudioStream.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ HotwordAudioStream(
+ @NonNull AudioFormat audioFormat,
+ @NonNull ParcelFileDescriptor audioStream,
+ @Nullable AudioTimestamp timestamp,
+ @NonNull PersistableBundle metadata) {
+ this.mAudioFormat = audioFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioFormat);
+ this.mAudioStream = audioStream;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioStream);
+ this.mTimestamp = timestamp;
+ this.mMetadata = metadata;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMetadata);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The {@link AudioFormat} of the audio stream.
+ */
+ @DataClass.Generated.Member
+ public @NonNull AudioFormat getAudioFormat() {
+ return mAudioFormat;
+ }
+
+ /**
+ * This stream starts with the audio bytes used for hotword detection, but continues streaming
+ * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull ParcelFileDescriptor getAudioStream() {
+ return mAudioStream;
+ }
+
+ /**
+ * The timestamp when the {@link #getAudioStream()} was captured by the Audio platform.
+ *
+ * <p>
+ * The {@link HotwordDetectionService} egressing the audio is the owner of the underlying
+ * AudioRecord. The {@link HotwordDetectionService} is expected to optionally populate this
+ * field by {@link AudioRecord#getTimestamp}.
+ * </p>
+ *
+ * <p>
+ * This timestamp can be used in conjunction with the
+ * {@link HotwordDetectedResult#getHotwordOffsetMillis()} and
+ * {@link HotwordDetectedResult#getHotwordDurationMillis()} to translate these durations to
+ * timestamps.
+ * </p>
+ */
+ @DataClass.Generated.Member
+ public @Nullable AudioTimestamp getTimestamp() {
+ return mTimestamp;
+ }
+
+ /**
+ * The metadata associated with the audio stream.
+ */
+ @DataClass.Generated.Member
+ public @NonNull PersistableBundle getMetadata() {
+ return mMetadata;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "HotwordAudioStream { " +
+ "audioFormat = " + mAudioFormat + ", " +
+ "audioStream = " + mAudioStream + ", " +
+ "timestamp = " + timestampToString() + ", " +
+ "metadata = " + mMetadata +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(HotwordAudioStream other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ HotwordAudioStream that = (HotwordAudioStream) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && Objects.equals(mAudioFormat, that.mAudioFormat)
+ && Objects.equals(mAudioStream, that.mAudioStream)
+ && Objects.equals(mTimestamp, that.mTimestamp)
+ && Objects.equals(mMetadata, that.mMetadata);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Objects.hashCode(mAudioFormat);
+ _hash = 31 * _hash + Objects.hashCode(mAudioStream);
+ _hash = 31 * _hash + Objects.hashCode(mTimestamp);
+ _hash = 31 * _hash + Objects.hashCode(mMetadata);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mTimestamp != null) flg |= 0x4;
+ dest.writeByte(flg);
+ dest.writeTypedObject(mAudioFormat, flags);
+ dest.writeTypedObject(mAudioStream, flags);
+ parcelTimestamp(dest, flags);
+ dest.writeTypedObject(mMetadata, flags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ HotwordAudioStream(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ AudioFormat audioFormat = (AudioFormat) in.readTypedObject(AudioFormat.CREATOR);
+ ParcelFileDescriptor audioStream = (ParcelFileDescriptor) in.readTypedObject(ParcelFileDescriptor.CREATOR);
+ AudioTimestamp timestamp = unparcelTimestamp(in);
+ PersistableBundle metadata = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
+
+ this.mAudioFormat = audioFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioFormat);
+ this.mAudioStream = audioStream;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioStream);
+ this.mTimestamp = timestamp;
+ this.mMetadata = metadata;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMetadata);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<HotwordAudioStream> CREATOR
+ = new Parcelable.Creator<HotwordAudioStream>() {
+ @Override
+ public HotwordAudioStream[] newArray(int size) {
+ return new HotwordAudioStream[size];
+ }
+
+ @Override
+ public HotwordAudioStream createFromParcel(@NonNull Parcel in) {
+ return new HotwordAudioStream(in);
+ }
+ };
+
+ /**
+ * A builder for {@link HotwordAudioStream}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private @NonNull AudioFormat mAudioFormat;
+ private @NonNull ParcelFileDescriptor mAudioStream;
+ private @Nullable AudioTimestamp mTimestamp;
+ private @NonNull PersistableBundle mMetadata;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param audioFormat
+ * The {@link AudioFormat} of the audio stream.
+ * @param audioStream
+ * This stream starts with the audio bytes used for hotword detection, but continues streaming
+ * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
+ */
+ public Builder(
+ @NonNull AudioFormat audioFormat,
+ @NonNull ParcelFileDescriptor audioStream) {
+ mAudioFormat = audioFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioFormat);
+ mAudioStream = audioStream;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioStream);
+ }
+
+ /**
+ * The {@link AudioFormat} of the audio stream.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setAudioFormat(@NonNull AudioFormat value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mAudioFormat = value;
+ return this;
+ }
+
+ /**
+ * This stream starts with the audio bytes used for hotword detection, but continues streaming
+ * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setAudioStream(@NonNull ParcelFileDescriptor value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mAudioStream = value;
+ return this;
+ }
+
+ /**
+ * The timestamp when the {@link #getAudioStream()} was captured by the Audio platform.
+ *
+ * <p>
+ * The {@link HotwordDetectionService} egressing the audio is the owner of the underlying
+ * AudioRecord. The {@link HotwordDetectionService} is expected to optionally populate this
+ * field by {@link AudioRecord#getTimestamp}.
+ * </p>
+ *
+ * <p>
+ * This timestamp can be used in conjunction with the
+ * {@link HotwordDetectedResult#getHotwordOffsetMillis()} and
+ * {@link HotwordDetectedResult#getHotwordDurationMillis()} to translate these durations to
+ * timestamps.
+ * </p>
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setTimestamp(@NonNull AudioTimestamp value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mTimestamp = value;
+ return this;
+ }
+
+ /**
+ * The metadata associated with the audio stream.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setMetadata(@NonNull PersistableBundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mMetadata = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull HotwordAudioStream build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mTimestamp = defaultTimestamp();
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mMetadata = defaultMetadata();
+ }
+ HotwordAudioStream o = new HotwordAudioStream(
+ mAudioFormat,
+ mAudioStream,
+ mTimestamp,
+ mMetadata);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1665463434564L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/service/voice/HotwordAudioStream.java",
+ inputSignatures = "private final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStream\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate static android.media.AudioTimestamp defaultTimestamp()\nprivate static android.os.PersistableBundle defaultMetadata()\nprivate java.lang.String timestampToString()\nprivate void parcelTimestamp(android.os.Parcel,int)\nprivate static @android.annotation.Nullable android.media.AudioTimestamp unparcelTimestamp(android.os.Parcel)\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java
index ab71459ed51e..6255d0060e3c 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -31,6 +31,8 @@ import com.android.internal.R;
import com.android.internal.util.DataClass;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -196,6 +198,15 @@ public final class HotwordDetectedResult implements Parcelable {
}
/**
+ * The list of the audio streams containing audio bytes that were used for hotword detection.
+ */
+ @NonNull
+ private final List<HotwordAudioStream> mAudioStreams;
+ private static List<HotwordAudioStream> defaultAudioStreams() {
+ return new ArrayList<>();
+ }
+
+ /**
* App-specific extras to support trigger.
*
* <p>The size of this bundle will be limited to {@link #getMaxBundleSize}. Results will larger
@@ -353,6 +364,11 @@ public final class HotwordDetectedResult implements Parcelable {
}
}
+ @DataClass.Suppress("addAudioStreams")
+ abstract static class BaseBuilder {
+
+ }
+
// Code below generated by codegen v1.0.23.
@@ -436,6 +452,7 @@ public final class HotwordDetectedResult implements Parcelable {
int score,
int personalizedScore,
int hotwordPhraseId,
+ @NonNull List<HotwordAudioStream> audioStreams,
@NonNull PersistableBundle extras) {
this.mConfidenceLevel = confidenceLevel;
com.android.internal.util.AnnotationValidations.validate(
@@ -448,6 +465,9 @@ public final class HotwordDetectedResult implements Parcelable {
this.mScore = score;
this.mPersonalizedScore = personalizedScore;
this.mHotwordPhraseId = hotwordPhraseId;
+ this.mAudioStreams = audioStreams;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioStreams);
this.mExtras = extras;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mExtras);
@@ -535,6 +555,14 @@ public final class HotwordDetectedResult implements Parcelable {
}
/**
+ * The list of the audio streams containing audio bytes that were used for hotword detection.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<HotwordAudioStream> getAudioStreams() {
+ return mAudioStreams;
+ }
+
+ /**
* App-specific extras to support trigger.
*
* <p>The size of this bundle will be limited to {@link #getMaxBundleSize}. Results will larger
@@ -578,6 +606,7 @@ public final class HotwordDetectedResult implements Parcelable {
"score = " + mScore + ", " +
"personalizedScore = " + mPersonalizedScore + ", " +
"hotwordPhraseId = " + mHotwordPhraseId + ", " +
+ "audioStreams = " + mAudioStreams + ", " +
"extras = " + mExtras +
" }";
}
@@ -604,6 +633,7 @@ public final class HotwordDetectedResult implements Parcelable {
&& mScore == that.mScore
&& mPersonalizedScore == that.mPersonalizedScore
&& mHotwordPhraseId == that.mHotwordPhraseId
+ && Objects.equals(mAudioStreams, that.mAudioStreams)
&& Objects.equals(mExtras, that.mExtras);
}
@@ -623,6 +653,7 @@ public final class HotwordDetectedResult implements Parcelable {
_hash = 31 * _hash + mScore;
_hash = 31 * _hash + mPersonalizedScore;
_hash = 31 * _hash + mHotwordPhraseId;
+ _hash = 31 * _hash + Objects.hashCode(mAudioStreams);
_hash = 31 * _hash + Objects.hashCode(mExtras);
return _hash;
}
@@ -645,6 +676,7 @@ public final class HotwordDetectedResult implements Parcelable {
dest.writeInt(mScore);
dest.writeInt(mPersonalizedScore);
dest.writeInt(mHotwordPhraseId);
+ dest.writeParcelableList(mAudioStreams, flags);
dest.writeTypedObject(mExtras, flags);
}
@@ -669,6 +701,8 @@ public final class HotwordDetectedResult implements Parcelable {
int score = in.readInt();
int personalizedScore = in.readInt();
int hotwordPhraseId = in.readInt();
+ List<HotwordAudioStream> audioStreams = new ArrayList<>();
+ in.readParcelableList(audioStreams, HotwordAudioStream.class.getClassLoader());
PersistableBundle extras = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
this.mConfidenceLevel = confidenceLevel;
@@ -682,6 +716,9 @@ public final class HotwordDetectedResult implements Parcelable {
this.mScore = score;
this.mPersonalizedScore = personalizedScore;
this.mHotwordPhraseId = hotwordPhraseId;
+ this.mAudioStreams = audioStreams;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioStreams);
this.mExtras = extras;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mExtras);
@@ -708,7 +745,7 @@ public final class HotwordDetectedResult implements Parcelable {
*/
@SuppressWarnings("WeakerAccess")
@DataClass.Generated.Member
- public static final class Builder {
+ public static final class Builder extends BaseBuilder {
private @HotwordConfidenceLevelValue int mConfidenceLevel;
private @Nullable MediaSyncEvent mMediaSyncEvent;
@@ -719,6 +756,7 @@ public final class HotwordDetectedResult implements Parcelable {
private int mScore;
private int mPersonalizedScore;
private int mHotwordPhraseId;
+ private @NonNull List<HotwordAudioStream> mAudioStreams;
private @NonNull PersistableBundle mExtras;
private long mBuilderFieldsSet = 0L;
@@ -843,6 +881,17 @@ public final class HotwordDetectedResult implements Parcelable {
}
/**
+ * The list of the audio streams containing audio bytes that were used for hotword detection.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setAudioStreams(@NonNull List<HotwordAudioStream> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x200;
+ mAudioStreams = value;
+ return this;
+ }
+
+ /**
* App-specific extras to support trigger.
*
* <p>The size of this bundle will be limited to {@link #getMaxBundleSize}. Results will larger
@@ -868,7 +917,7 @@ public final class HotwordDetectedResult implements Parcelable {
@DataClass.Generated.Member
public @NonNull Builder setExtras(@NonNull PersistableBundle value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x200;
+ mBuilderFieldsSet |= 0x400;
mExtras = value;
return this;
}
@@ -876,7 +925,7 @@ public final class HotwordDetectedResult implements Parcelable {
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull HotwordDetectedResult build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x400; // Mark builder used
+ mBuilderFieldsSet |= 0x800; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mConfidenceLevel = defaultConfidenceLevel();
@@ -906,6 +955,9 @@ public final class HotwordDetectedResult implements Parcelable {
mHotwordPhraseId = defaultHotwordPhraseId();
}
if ((mBuilderFieldsSet & 0x200) == 0) {
+ mAudioStreams = defaultAudioStreams();
+ }
+ if ((mBuilderFieldsSet & 0x400) == 0) {
mExtras = defaultExtras();
}
HotwordDetectedResult o = new HotwordDetectedResult(
@@ -918,12 +970,13 @@ public final class HotwordDetectedResult implements Parcelable {
mScore,
mPersonalizedScore,
mHotwordPhraseId,
+ mAudioStreams,
mExtras);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x400) != 0) {
+ if ((mBuilderFieldsSet & 0x800) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -931,10 +984,10 @@ public final class HotwordDetectedResult implements Parcelable {
}
@DataClass.Generated(
- time = 1658357814396L,
+ time = 1664876310951L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java",
- inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\npublic static final java.lang.String EXTRA_PROXIMITY_METERS\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\npublic static final java.lang.String EXTRA_PROXIMITY_METERS\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> mAudioStreams\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static java.util.List<android.service.voice.HotwordAudioStream> defaultAudioStreams()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index cb8f0af10c47..d1f05ec0c05e 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -96,13 +96,6 @@ public class FeatureFlagUtils {
/** @hide */
public static final String SETTINGS_AUTO_TEXT_WRAPPING = "settings_auto_text_wrapping";
- /** Flag to enable / disable the Simple Cursor accessibility feature in
- * Settings.
- * @hide
- */
- public static final String SETTINGS_ACCESSIBILITY_SIMPLE_CURSOR =
- "settings_accessibility_simple_cursor";
-
/**
* Enable new language and keyboard settings UI
* @hide
@@ -149,7 +142,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true");
DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true");
DEFAULT_FLAGS.put(SETTINGS_AUTO_TEXT_WRAPPING, "false");
- DEFAULT_FLAGS.put(SETTINGS_ACCESSIBILITY_SIMPLE_CURSOR, "false");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_UI, "false");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA, "false");
DEFAULT_FLAGS.put(SETTINGS_ADB_METRICS_WRITER, "false");
diff --git a/core/java/android/view/DisplayAddress.java b/core/java/android/view/DisplayAddress.java
index 91a24c64d37c..99e811a72605 100644
--- a/core/java/android/view/DisplayAddress.java
+++ b/core/java/android/view/DisplayAddress.java
@@ -33,7 +33,7 @@ public abstract class DisplayAddress implements Parcelable {
*
* @param physicalDisplayId A physical display ID.
* @return The {@link Physical} address.
- * @see SurfaceControl#getPhysicalDisplayIds
+ * @see com.android.server.display.DisplayControl#getPhysicalDisplayIds
*/
@NonNull
public static Physical fromPhysicalDisplayId(long physicalDisplayId) {
@@ -83,7 +83,7 @@ public abstract class DisplayAddress implements Parcelable {
* Stable display ID combining port and model.
*
* @return An ID in the range [0, 2^64) interpreted as signed.
- * @see SurfaceControl#getPhysicalDisplayIds
+ * @see com.android.server.display.DisplayControl#getPhysicalDisplayIds
*/
public long getPhysicalDisplayId() {
return mPhysicalDisplayId;
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 9fd8ecb1727f..9b1d8673390b 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -80,6 +80,7 @@ public final class InputDevice implements Parcelable {
private final boolean mHasButtonUnderPad;
private final boolean mHasSensor;
private final boolean mHasBattery;
+ private final boolean mSupportsUsi;
private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
@GuardedBy("mMotionRanges")
@@ -462,7 +463,7 @@ public final class InputDevice implements Parcelable {
int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
KeyCharacterMap keyCharacterMap, @InputDeviceCountryCode int countryCode,
boolean hasVibrator, boolean hasMicrophone, boolean hasButtonUnderPad,
- boolean hasSensor, boolean hasBattery) {
+ boolean hasSensor, boolean hasBattery, boolean supportsUsi) {
mId = id;
mGeneration = generation;
mControllerNumber = controllerNumber;
@@ -481,6 +482,7 @@ public final class InputDevice implements Parcelable {
mHasSensor = hasSensor;
mHasBattery = hasBattery;
mIdentifier = new InputDeviceIdentifier(descriptor, vendorId, productId);
+ mSupportsUsi = supportsUsi;
}
private InputDevice(Parcel in) {
@@ -501,6 +503,7 @@ public final class InputDevice implements Parcelable {
mHasButtonUnderPad = in.readInt() != 0;
mHasSensor = in.readInt() != 0;
mHasBattery = in.readInt() != 0;
+ mSupportsUsi = in.readInt() != 0;
mIdentifier = new InputDeviceIdentifier(mDescriptor, mVendorId, mProductId);
int numRanges = in.readInt();
@@ -538,6 +541,7 @@ public final class InputDevice implements Parcelable {
private boolean mHasBattery = false;
@InputDeviceCountryCode
private int mCountryCode = InputDeviceCountryCode.INVALID;
+ private boolean mSupportsUsi = false;
/** @see InputDevice#getId() */
public Builder setId(int id) {
@@ -641,12 +645,18 @@ public final class InputDevice implements Parcelable {
return this;
}
+ /** @see InputDevice#supportsUsi() () */
+ public Builder setSupportsUsi(boolean supportsUsi) {
+ mSupportsUsi = supportsUsi;
+ return this;
+ }
+
/** Build {@link InputDevice}. */
public InputDevice build() {
return new InputDevice(mId, mGeneration, mControllerNumber, mName, mVendorId,
mProductId, mDescriptor, mIsExternal, mSources, mKeyboardType, mKeyCharacterMap,
mCountryCode, mHasVibrator, mHasMicrophone, mHasButtonUnderPad, mHasSensor,
- mHasBattery);
+ mHasBattery, mSupportsUsi);
}
}
@@ -1179,6 +1189,15 @@ public final class InputDevice implements Parcelable {
}
/**
+ * Reports whether the device supports the Universal Stylus Initiative (USI) protocol for
+ * styluses.
+ * @hide
+ */
+ public boolean supportsUsi() {
+ return mSupportsUsi;
+ }
+
+ /**
* Provides information about the range of values for a particular {@link MotionEvent} axis.
*
* @see InputDevice#getMotionRange(int)
@@ -1308,6 +1327,7 @@ public final class InputDevice implements Parcelable {
out.writeInt(mHasButtonUnderPad ? 1 : 0);
out.writeInt(mHasSensor ? 1 : 0);
out.writeInt(mHasBattery ? 1 : 0);
+ out.writeInt(mSupportsUsi ? 1 : 0);
final int numRanges = mMotionRanges.size();
out.writeInt(numRanges);
@@ -1361,6 +1381,8 @@ public final class InputDevice implements Parcelable {
description.append(" Has mic: ").append(mHasMicrophone).append("\n");
+ description.append(" Supports USI: ").append(mSupportsUsi).append("\n");
+
description.append(" Sources: 0x").append(Integer.toHexString(mSources)).append(" (");
appendSourceDescriptionIfApplicable(description, SOURCE_KEYBOARD, "keyboard");
appendSourceDescriptionIfApplicable(description, SOURCE_DPAD, "dpad");
diff --git a/core/java/android/view/InsetsFrameProvider.java b/core/java/android/view/InsetsFrameProvider.java
index eb8687c47bed..72757807169e 100644
--- a/core/java/android/view/InsetsFrameProvider.java
+++ b/core/java/android/view/InsetsFrameProvider.java
@@ -59,6 +59,7 @@ public class InsetsFrameProvider implements Parcelable {
private static final int HAS_INSETS_SIZE_OVERRIDE = 2;
private static Rect sTmpRect = new Rect();
+ private static Rect sTmpRect2 = new Rect();
/**
* The type of insets to provide.
@@ -88,6 +89,18 @@ public class InsetsFrameProvider implements Parcelable {
*/
public InsetsSizeOverride[] insetsSizeOverrides = null;
+ /**
+ * This field, if set, is indicating the insets needs to be at least the given size inside the
+ * display cutout safe area. This will be compared to the insets size calculated based on other
+ * attributes, and will be applied when this is larger. This is independent of the
+ * PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT in LayoutParams, as this is not going to change
+ * the layout of the window, but only change the insets frame. This can be applied to insets
+ * calculated based on all three source frames.
+ *
+ * Be cautious, this will not be in effect for the window types whose insets size is overridden.
+ */
+ public Insets minimalInsetsSizeInDisplayCutoutSafe = null;
+
public InsetsFrameProvider(int type) {
this(type, SOURCE_FRAME, null, null);
}
@@ -202,7 +215,8 @@ public class InsetsFrameProvider implements Parcelable {
public static void calculateInsetsFrame(Rect displayFrame, Rect containerBounds,
Rect displayCutoutSafe, Rect inOutFrame, int source, Insets insetsSize,
- @WindowManager.LayoutParams.PrivateFlags int privateFlags) {
+ @WindowManager.LayoutParams.PrivateFlags int privateFlags,
+ Insets displayCutoutSafeInsetsSize) {
boolean extendByCutout = false;
if (source == InsetsFrameProvider.SOURCE_DISPLAY) {
inOutFrame.set(displayFrame);
@@ -214,6 +228,33 @@ public class InsetsFrameProvider implements Parcelable {
if (insetsSize == null) {
return;
}
+ if (displayCutoutSafeInsetsSize != null) {
+ sTmpRect2.set(inOutFrame);
+ }
+ calculateInsetsFrame(inOutFrame, insetsSize);
+
+ if (extendByCutout) {
+ WindowLayout.extendFrameByCutout(displayCutoutSafe, displayFrame, inOutFrame, sTmpRect);
+ }
+
+ if (displayCutoutSafeInsetsSize != null) {
+ // The insets is at least with the given size within the display cutout safe area.
+ // Calculate the smallest size.
+ calculateInsetsFrame(sTmpRect2, displayCutoutSafeInsetsSize);
+ WindowLayout.extendFrameByCutout(displayCutoutSafe, displayFrame, sTmpRect2, sTmpRect);
+ // If it's larger than previous calculation, use it.
+ if (sTmpRect2.contains(inOutFrame)) {
+ inOutFrame.set(sTmpRect2);
+ }
+ }
+ }
+
+ /**
+ * Calculate the insets frame given the insets size and the source frame.
+ * @param inOutFrame the source frame.
+ * @param insetsSize the insets size. Only the first non-zero value will be taken.
+ */
+ private static void calculateInsetsFrame(Rect inOutFrame, Insets insetsSize) {
// Only one side of the provider shall be applied. Check in the order of left - top -
// right - bottom, only the first non-zero value will be applied.
if (insetsSize.left != 0) {
@@ -227,10 +268,6 @@ public class InsetsFrameProvider implements Parcelable {
} else {
inOutFrame.setEmpty();
}
-
- if (extendByCutout) {
- WindowLayout.extendFrameByCutout(displayCutoutSafe, displayFrame, inOutFrame, sTmpRect);
- }
}
/**
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3ffb78161da1..c46f33a4a786 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -110,7 +110,7 @@ public final class SurfaceControl implements Parcelable {
private static native long nativeReadFromParcel(Parcel in);
private static native long nativeCopyFromSurfaceControl(long nativeObject);
private static native void nativeWriteToParcel(long nativeObject, Parcel out);
- private static native void nativeRelease(long nativeObject);
+ private static native long nativeGetNativeSurfaceControlFinalizer();
private static native void nativeDisconnect(long nativeObject);
private static native void nativeUpdateDefaultBufferSize(long nativeObject, int width, int height);
@@ -173,8 +173,6 @@ public final class SurfaceControl implements Parcelable {
private static native boolean nativeClearAnimationFrameStats();
private static native boolean nativeGetAnimationFrameStats(WindowAnimationFrameStats outStats);
- private static native long[] nativeGetPhysicalDisplayIds();
- private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
private static native void nativeSetDisplaySurface(long transactionObj,
IBinder displayToken, long nativeSurfaceObject);
private static native void nativeSetDisplayLayerStack(long transactionObj,
@@ -464,6 +462,12 @@ public final class SurfaceControl implements Parcelable {
static GlobalTransactionWrapper sGlobalTransaction;
static long sTransactionNestCount = 0;
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(SurfaceControl.class.getClassLoader(),
+ nativeGetNativeSurfaceControlFinalizer());
+
+ private Runnable mFreeNativeResources;
+
/**
* Adds a reparenting listener.
*
@@ -722,6 +726,8 @@ public final class SurfaceControl implements Parcelable {
}
if (nativeObject != 0) {
mCloseGuard.openWithCallSite("release", callsite);
+ mFreeNativeResources =
+ sRegistry.registerNativeAllocation(this, nativeObject);
}
mNativeObject = nativeObject;
mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
@@ -1150,6 +1156,7 @@ public final class SurfaceControl implements Parcelable {
mHeight = h;
mLocalOwnerView = localOwnerView;
Parcel metaParcel = Parcel.obtain();
+ long nativeObject = 0;
try {
if (metadata != null && metadata.size() > 0) {
metaParcel.writeInt(metadata.size());
@@ -1161,17 +1168,16 @@ public final class SurfaceControl implements Parcelable {
}
metaParcel.setDataPosition(0);
}
- mNativeObject = nativeCreate(session, name, w, h, format, flags,
+ nativeObject = nativeCreate(session, name, w, h, format, flags,
parent != null ? parent.mNativeObject : 0, metaParcel);
} finally {
metaParcel.recycle();
}
- if (mNativeObject == 0) {
+ if (nativeObject == 0) {
throw new OutOfResourcesException(
"Couldn't allocate SurfaceControl native object");
}
- mNativeHandle = nativeGetHandle(mNativeObject);
- mCloseGuard.openWithCallSite("release", callsite);
+ assignNativeObject(nativeObject, callsite);
}
/**
@@ -1283,9 +1289,6 @@ public final class SurfaceControl implements Parcelable {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
- if (mNativeObject != 0) {
- nativeRelease(mNativeObject);
- }
} finally {
super.finalize();
}
@@ -1303,7 +1306,7 @@ public final class SurfaceControl implements Parcelable {
*/
public void release() {
if (mNativeObject != 0) {
- nativeRelease(mNativeObject);
+ mFreeNativeResources.run();
mNativeObject = 0;
mNativeHandle = 0;
mCloseGuard.close();
@@ -2035,35 +2038,6 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * @hide
- */
- public static long[] getPhysicalDisplayIds() {
- return nativeGetPhysicalDisplayIds();
- }
-
- /**
- * @hide
- */
- public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
- return nativeGetPhysicalDisplayToken(physicalDisplayId);
- }
-
- /**
- * TODO(b/116025192): Remove this stopgap once framework is display-agnostic.
- *
- * @hide
- */
- @TestApi
- @NonNull
- public static IBinder getInternalDisplayToken() {
- final long[] physicalDisplayIds = getPhysicalDisplayIds();
- if (physicalDisplayIds.length == 0) {
- return null;
- }
- return getPhysicalDisplayToken(physicalDisplayIds[0]);
- }
-
- /**
* Returns whether protected content is supported in GPU composition.
* @hide
*/
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 331aa59a2fd8..7b6ebf7cd9b8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12538,6 +12538,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (!enabled) {
cancelPendingInputEvents();
}
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED);
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1e2b241fbb51..b2b5f1356273 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1101,7 +1101,7 @@ public final class ViewRootImpl implements ViewParent,
// Update the last resource config in case the resource configuration was changed while
// activity relaunched.
- mLastConfigurationFromResources.setTo(getConfiguration());
+ updateLastConfigurationFromResources(getConfiguration());
}
private Configuration getConfiguration() {
@@ -5424,13 +5424,7 @@ public final class ViewRootImpl implements ViewParent,
// Update the display with new DisplayAdjustments.
updateInternalDisplay(mDisplay.getDisplayId(), localResources);
- final int lastLayoutDirection = mLastConfigurationFromResources.getLayoutDirection();
- final int currentLayoutDirection = config.getLayoutDirection();
- mLastConfigurationFromResources.setTo(config);
- if (lastLayoutDirection != currentLayoutDirection
- && mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
- mView.setLayoutDirection(currentLayoutDirection);
- }
+ updateLastConfigurationFromResources(config);
mView.dispatchConfigurationChanged(config);
// We could have gotten this {@link Configuration} update after we called
@@ -5444,6 +5438,17 @@ public final class ViewRootImpl implements ViewParent,
updateForceDarkMode();
}
+ private void updateLastConfigurationFromResources(Configuration resConfig) {
+ final int lastLayoutDirection = mLastConfigurationFromResources.getLayoutDirection();
+ final int currentLayoutDirection = resConfig.getLayoutDirection();
+ mLastConfigurationFromResources.setTo(resConfig);
+ // Update layout direction in case the language or screen layout is changed.
+ if (lastLayoutDirection != currentLayoutDirection && mView != null
+ && mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
+ mView.setLayoutDirection(currentLayoutDirection);
+ }
+ }
+
/**
* Return true if child is an ancestor of parent, (or equal to the parent).
*/
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e757d6a33dd1..cc85181f52f2 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -777,12 +777,6 @@ public interface WindowManager extends ViewManager {
int TAKE_SCREENSHOT_FULLSCREEN = 1;
/**
- * Invoke screenshot flow allowing the user to select a region.
- * @hide
- */
- int TAKE_SCREENSHOT_SELECTED_REGION = 2;
-
- /**
* Invoke screenshot flow with an image provided by the caller.
* @hide
*/
@@ -794,7 +788,6 @@ public interface WindowManager extends ViewManager {
* @hide
*/
@IntDef({TAKE_SCREENSHOT_FULLSCREEN,
- TAKE_SCREENSHOT_SELECTED_REGION,
TAKE_SCREENSHOT_PROVIDED_IMAGE})
@interface ScreenshotType {}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index f86f51fc0022..b0cf504ec568 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -709,6 +709,18 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
*/
public static final int CONTENT_CHANGE_TYPE_ERROR = 0x0000800;
+ /**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+ * The source node changed its ability to interact returned by
+ * {@link AccessibilityNodeInfo#isEnabled}.
+ * The view changing content's ability to interact should call
+ * {@link AccessibilityNodeInfo#setEnabled} and then send this event.
+ *
+ * @see AccessibilityNodeInfo#isEnabled
+ * @see AccessibilityNodeInfo#setEnabled
+ */
+ public static final int CONTENT_CHANGE_TYPE_ENABLED = 1 << 12;
+
/** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
public static final int SPEECH_STATE_SPEAKING_START = 0x00000001;
@@ -836,6 +848,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
CONTENT_CHANGE_TYPE_DRAG_CANCELLED,
CONTENT_CHANGE_TYPE_CONTENT_INVALID,
CONTENT_CHANGE_TYPE_ERROR,
+ CONTENT_CHANGE_TYPE_ENABLED,
})
public @interface ContentChangeTypes {}
@@ -1105,6 +1118,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
case CONTENT_CHANGE_TYPE_CONTENT_INVALID:
return "CONTENT_CHANGE_TYPE_CONTENT_INVALID";
case CONTENT_CHANGE_TYPE_ERROR: return "CONTENT_CHANGE_TYPE_ERROR";
+ case CONTENT_CHANGE_TYPE_ENABLED: return "CONTENT_CHANGE_TYPE_ENABLED";
default: return Integer.toHexString(type);
}
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index dbefcfb5ef17..70cfc3efc88a 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -543,7 +543,6 @@ public final class AutofillManager {
* {@link com.android.server.autofill.PresentationStatsEventLogger#getNoPresentationEventReason(int)}
* as well.</p>
*
- * TODO(b/233833662): Expose this as a public API in U.
* @hide
*/
@IntDef(prefix = { "COMMIT_REASON_" }, value = {
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
index a8e1d758cb61..01e8b3412614 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
@@ -17,6 +17,7 @@
package android.view.inputmethod;
import android.annotation.AnyThread;
+import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -274,4 +275,14 @@ final class IInputMethodManagerInvoker {
throw e.rethrowFromSystemServer();
}
}
+
+ @AnyThread
+ void setStylusWindowIdleTimeoutForTest(
+ IInputMethodClient client, @DurationMillisLong long timeout) {
+ try {
+ mTarget.setStylusWindowIdleTimeoutForTest(client, timeout);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 978bfc7af60d..4d5a17d92f11 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -16,6 +16,7 @@
package android.view.inputmethod;
+import android.annotation.DurationMillisLong;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -417,4 +418,12 @@ public interface InputMethod {
default void removeStylusHandwritingWindow() {
// intentionally empty
}
+
+ /**
+ * Set a stylus idle-timeout after which handwriting {@code InkWindow} will be removed.
+ * @hide
+ */
+ default void setStylusWindowIdleTimeoutForTest(@DurationMillisLong long timeout) {
+ // intentionally empty
+ }
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index c1fe6866fb4d..d0405f0e5991 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -36,8 +36,10 @@ import static com.android.internal.inputmethod.StartInputReason.BOUND_TO_IMMS;
import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION;
import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION;
+import android.Manifest;
import android.annotation.DisplayContext;
import android.annotation.DrawableRes;
+import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
@@ -265,6 +267,12 @@ import java.util.function.Consumer;
* they can switch to it, to confirm with the system that they know about it
* and want to make it available for use.</p>
* </ul>
+ *
+ * <p>If your app targets Android 11 (API level 30) or higher, the methods in
+ * this class each return a filtered result by the rules of
+ * <a href="/training/basics/intents/package-visibility">package visibility</a>,
+ * except for the currently connected IME. Apps having a query for the
+ * {@link InputMethod#SERVICE_INTERFACE} see all IMEs.</p>
*/
@SystemService(Context.INPUT_METHOD_SERVICE)
@RequiresFeature(PackageManager.FEATURE_INPUT_METHODS)
@@ -2587,6 +2595,20 @@ public final class InputMethodManager {
}
/**
+ * Set a stylus idle-timeout after which handwriting {@code InkWindow} will be removed.
+ * <p> This API is for tests only.</p>
+ * @param timeout to set in milliseconds. To reset to default, use a value <= zero.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
+ @TestApi
+ public void setStylusWindowIdleTimeoutForTest(@DurationMillisLong long timeout) {
+ synchronized (mH) {
+ mServiceInvoker.setStylusWindowIdleTimeoutForTest(mClient, timeout);
+ }
+ }
+
+ /**
* An empty method only to avoid crashes of apps that call this method via reflection and do not
* handle {@link NoSuchMethodException} in a graceful manner.
*
diff --git a/core/java/com/android/internal/inputmethod/IInputMethod.aidl b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
index 9182d1dc56bf..c62fba9fb9b8 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethod.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
@@ -87,4 +87,6 @@ oneway interface IInputMethod {
void finishStylusHandwriting();
void removeStylusHandwritingWindow();
+
+ void setStylusWindowIdleTimeoutForTest(long timeout);
}
diff --git a/core/java/com/android/internal/util/ObservableServiceConnection.java b/core/java/com/android/internal/util/ObservableServiceConnection.java
new file mode 100644
index 000000000000..3165d293bd91
--- /dev/null
+++ b/core/java/com/android/internal/util/ObservableServiceConnection.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2022 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.util;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.CallbackRegistry.NotifierCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * {@link ObservableServiceConnection} is a concrete implementation of {@link ServiceConnection}
+ * that enables monitoring the status of a binder connection. It also aides in automatically
+ * converting a proxy into an internal wrapper type.
+ *
+ * @param <T> The type of the wrapper over the resulting service.
+ */
+public class ObservableServiceConnection<T> implements ServiceConnection {
+ /**
+ * An interface for converting the service proxy into a given internal wrapper type.
+ *
+ * @param <T> The type of the wrapper over the resulting service.
+ */
+ public interface ServiceTransformer<T> {
+ /**
+ * Called to convert the service proxy to the wrapper type.
+ *
+ * @param service The service proxy to create the wrapper type from.
+ * @return The wrapper type.
+ */
+ T convert(IBinder service);
+ }
+
+ /**
+ * An interface for listening to the connection status.
+ *
+ * @param <T> The wrapper type.
+ */
+ public interface Callback<T> {
+ /**
+ * Invoked when the service has been successfully connected to.
+ *
+ * @param connection The {@link ObservableServiceConnection} instance that is now connected
+ * @param service The service proxy converted into the typed wrapper.
+ */
+ void onConnected(ObservableServiceConnection<T> connection, T service);
+
+ /**
+ * Invoked when the service has been disconnected.
+ *
+ * @param connection The {@link ObservableServiceConnection} that is now disconnected.
+ * @param reason The reason for the disconnection.
+ */
+ void onDisconnected(ObservableServiceConnection<T> connection,
+ @DisconnectReason int reason);
+ }
+
+ /**
+ * Default state, service has not yet disconnected.
+ */
+ public static final int DISCONNECT_REASON_NONE = 0;
+ /**
+ * Disconnection was due to the resulting binding being {@code null}.
+ */
+ public static final int DISCONNECT_REASON_NULL_BINDING = 1;
+ /**
+ * Disconnection was due to the remote end disconnecting.
+ */
+ public static final int DISCONNECT_REASON_DISCONNECTED = 2;
+ /**
+ * Disconnection due to the binder dying.
+ */
+ public static final int DISCONNECT_REASON_BINDING_DIED = 3;
+ /**
+ * Disconnection from an explicit unbinding.
+ */
+ public static final int DISCONNECT_REASON_UNBIND = 4;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ DISCONNECT_REASON_NONE,
+ DISCONNECT_REASON_NULL_BINDING,
+ DISCONNECT_REASON_DISCONNECTED,
+ DISCONNECT_REASON_BINDING_DIED,
+ DISCONNECT_REASON_UNBIND
+ })
+ public @interface DisconnectReason {
+ }
+
+ private final Object mLock = new Object();
+ private final Context mContext;
+ private final Executor mExecutor;
+ private final ServiceTransformer<T> mTransformer;
+ private final Intent mServiceIntent;
+ private final int mFlags;
+
+ @GuardedBy("mLock")
+ private T mService;
+ @GuardedBy("mLock")
+ private boolean mBoundCalled = false;
+ @GuardedBy("mLock")
+ private int mLastDisconnectReason = DISCONNECT_REASON_NONE;
+
+ private final CallbackRegistry<Callback<T>, ObservableServiceConnection<T>, T>
+ mCallbackRegistry = new CallbackRegistry<>(
+ new NotifierCallback<Callback<T>, ObservableServiceConnection<T>, T>() {
+ @Override
+ public void onNotifyCallback(Callback<T> callback,
+ ObservableServiceConnection<T> sender,
+ int disconnectReason, T service) {
+ mExecutor.execute(() -> {
+ synchronized (mLock) {
+ if (service != null) {
+ callback.onConnected(sender, service);
+ } else if (mLastDisconnectReason != DISCONNECT_REASON_NONE) {
+ callback.onDisconnected(sender, disconnectReason);
+ }
+ }
+ });
+ }
+ });
+
+ /**
+ * Default constructor for {@link ObservableServiceConnection}.
+ *
+ * @param context The context from which the service will be bound with.
+ * @param executor The executor for connection callbacks to be delivered on
+ * @param transformer A {@link ObservableServiceConnection.ServiceTransformer} for transforming
+ * the resulting service into a desired type.
+ */
+ public ObservableServiceConnection(@NonNull Context context,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull ServiceTransformer<T> transformer,
+ Intent serviceIntent,
+ int flags) {
+ mContext = context;
+ mExecutor = executor;
+ mTransformer = transformer;
+ mServiceIntent = serviceIntent;
+ mFlags = flags;
+ }
+
+ /**
+ * Initiate binding to the service.
+ *
+ * @return {@code true} if initiating binding succeed, {@code false} if the binding failed or
+ * if this service is already bound. Regardless of the return value, you should later call
+ * {@link #unbind()} to release the connection.
+ */
+ public boolean bind() {
+ synchronized (mLock) {
+ if (mBoundCalled) {
+ return false;
+ }
+ final boolean bindResult =
+ mContext.bindService(mServiceIntent, mFlags, mExecutor, this);
+ mBoundCalled = true;
+ return bindResult;
+ }
+ }
+
+ /**
+ * Disconnect from the service if bound.
+ */
+ public void unbind() {
+ onDisconnected(DISCONNECT_REASON_UNBIND);
+ }
+
+ /**
+ * Adds a callback for receiving connection updates.
+ *
+ * @param callback The {@link Callback} to receive future updates.
+ */
+ public void addCallback(Callback<T> callback) {
+ mCallbackRegistry.add(callback);
+ mExecutor.execute(() -> {
+ synchronized (mLock) {
+ if (mService != null) {
+ callback.onConnected(this, mService);
+ } else if (mLastDisconnectReason != DISCONNECT_REASON_NONE) {
+ callback.onDisconnected(this, mLastDisconnectReason);
+ }
+ }
+ });
+ }
+
+ /**
+ * Removes previously added callback from receiving future connection updates.
+ *
+ * @param callback The {@link Callback} to be removed.
+ */
+ public void removeCallback(Callback<T> callback) {
+ synchronized (mLock) {
+ mCallbackRegistry.remove(callback);
+ }
+ }
+
+ private void onDisconnected(@DisconnectReason int reason) {
+ synchronized (mLock) {
+ if (!mBoundCalled) {
+ return;
+ }
+ mBoundCalled = false;
+ mLastDisconnectReason = reason;
+ mContext.unbindService(this);
+ mService = null;
+ mCallbackRegistry.notifyCallbacks(this, reason, null);
+ }
+ }
+
+ @Override
+ public final void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ mService = mTransformer.convert(service);
+ mLastDisconnectReason = DISCONNECT_REASON_NONE;
+ mCallbackRegistry.notifyCallbacks(this, mLastDisconnectReason, mService);
+ }
+ }
+
+ @Override
+ public final void onServiceDisconnected(ComponentName name) {
+ onDisconnected(DISCONNECT_REASON_DISCONNECTED);
+ }
+
+ @Override
+ public final void onBindingDied(ComponentName name) {
+ onDisconnected(DISCONNECT_REASON_BINDING_DIED);
+ }
+
+ @Override
+ public final void onNullBinding(ComponentName name) {
+ onDisconnected(DISCONNECT_REASON_NULL_BINDING);
+ }
+}
diff --git a/core/java/com/android/internal/util/PersistentServiceConnection.java b/core/java/com/android/internal/util/PersistentServiceConnection.java
new file mode 100644
index 000000000000..d2017347bd64
--- /dev/null
+++ b/core/java/com/android/internal/util/PersistentServiceConnection.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2022 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.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.SystemClock;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.concurrent.Executor;
+
+/**
+ * {@link PersistentServiceConnection} is a concrete implementation of {@link ServiceConnection}
+ * that maintains the binder connection by handling reconnection when a failure occurs.
+ *
+ * @param <T> The transformed connection type handled by the service.
+ *
+ * When the target process is killed (by OOM-killer, force-stopped, crash, etc..) then this class
+ * will trigger a reconnection to the target. This should be used carefully.
+ *
+ * NOTE: This class does *not* handle package-updates -- i.e. even if the binding dies due to
+ * the target package being updated, this class won't reconnect. This is because this class doesn't
+ * know what to do when the service component has gone missing, for example. If the user of this
+ * class wants to restore the connection, then it should call {@link #unbind()} and {@link #bind}
+ * explicitly.
+ */
+public class PersistentServiceConnection<T> extends ObservableServiceConnection<T> {
+ private final Callback<T> mConnectionCallback = new Callback<T>() {
+ private long mConnectedTime;
+
+ @Override
+ public void onConnected(ObservableServiceConnection<T> connection, T service) {
+ mConnectedTime = mInjector.uptimeMillis();
+ }
+
+ @Override
+ public void onDisconnected(ObservableServiceConnection<T> connection,
+ @DisconnectReason int reason) {
+ if (reason == DISCONNECT_REASON_UNBIND) return;
+ synchronized (mLock) {
+ if ((mInjector.uptimeMillis() - mConnectedTime) > mMinConnectionDurationMs) {
+ mReconnectAttempts = 0;
+ bindInternalLocked();
+ } else {
+ scheduleConnectionAttemptLocked();
+ }
+ }
+ }
+ };
+
+ private final Object mLock = new Object();
+ private final Injector mInjector;
+ private final Handler mHandler;
+ private final int mMinConnectionDurationMs;
+ private final int mMaxReconnectAttempts;
+ private final int mBaseReconnectDelayMs;
+ @GuardedBy("mLock")
+ private int mReconnectAttempts;
+ @GuardedBy("mLock")
+ private Object mCancelToken;
+
+ private final Runnable mConnectRunnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ mCancelToken = null;
+ bindInternalLocked();
+ }
+ }
+ };
+
+ /**
+ * Default constructor for {@link PersistentServiceConnection}.
+ *
+ * @param context The context from which the service will be bound with.
+ * @param executor The executor for connection callbacks to be delivered on
+ * @param transformer A {@link ServiceTransformer} for transforming
+ */
+ public PersistentServiceConnection(Context context,
+ Executor executor,
+ Handler handler,
+ ServiceTransformer<T> transformer,
+ Intent serviceIntent,
+ int flags,
+ int minConnectionDurationMs,
+ int maxReconnectAttempts,
+ int baseReconnectDelayMs) {
+ this(context,
+ executor,
+ handler,
+ transformer,
+ serviceIntent,
+ flags,
+ minConnectionDurationMs,
+ maxReconnectAttempts,
+ baseReconnectDelayMs,
+ new Injector());
+ }
+
+ @VisibleForTesting
+ public PersistentServiceConnection(
+ Context context,
+ Executor executor,
+ Handler handler,
+ ServiceTransformer<T> transformer,
+ Intent serviceIntent,
+ int flags,
+ int minConnectionDurationMs,
+ int maxReconnectAttempts,
+ int baseReconnectDelayMs,
+ Injector injector) {
+ super(context, executor, transformer, serviceIntent, flags);
+ mHandler = handler;
+ mMinConnectionDurationMs = minConnectionDurationMs;
+ mMaxReconnectAttempts = maxReconnectAttempts;
+ mBaseReconnectDelayMs = baseReconnectDelayMs;
+ mInjector = injector;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean bind() {
+ synchronized (mLock) {
+ addCallback(mConnectionCallback);
+ mReconnectAttempts = 0;
+ return bindInternalLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean bindInternalLocked() {
+ return super.bind();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void unbind() {
+ synchronized (mLock) {
+ removeCallback(mConnectionCallback);
+ cancelPendingConnectionAttemptLocked();
+ super.unbind();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void cancelPendingConnectionAttemptLocked() {
+ if (mCancelToken != null) {
+ mHandler.removeCallbacksAndMessages(mCancelToken);
+ mCancelToken = null;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void scheduleConnectionAttemptLocked() {
+ cancelPendingConnectionAttemptLocked();
+
+ if (mReconnectAttempts >= mMaxReconnectAttempts) {
+ return;
+ }
+
+ final long reconnectDelayMs =
+ (long) Math.scalb(mBaseReconnectDelayMs, mReconnectAttempts);
+
+ mCancelToken = new Object();
+ mHandler.postDelayed(mConnectRunnable, mCancelToken, reconnectDelayMs);
+ mReconnectAttempts++;
+ }
+
+ /**
+ * Injector for testing
+ */
+ @VisibleForTesting
+ public static class Injector {
+ /**
+ * Returns milliseconds since boot, not counting time spent in deep sleep. Can be overridden
+ * in tests with a fake clock.
+ */
+ public long uptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index f4c3928ac72f..955895ffdae2 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -150,4 +150,10 @@ interface IInputMethodManager {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.INJECT_EVENTS)")
void addVirtualStylusIdForTestSession(in IInputMethodClient client);
+
+ /** Set a stylus idle-timeout after which handwriting {@code InkWindow} will be removed. */
+ @EnforcePermission("TEST_INPUT_METHOD")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ + "android.Manifest.permission.TEST_INPUT_METHOD)")
+ void setStylusWindowIdleTimeoutForTest(in IInputMethodClient client, long timeout);
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index af9c5a5cc0d5..52ffc984c41e 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -17,6 +17,9 @@
package com.android.internal.widget;
+import static android.content.res.Resources.ID_NULL;
+
+import android.annotation.IdRes;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -96,6 +99,8 @@ public class ResolverDrawerLayout extends ViewGroup {
private int mTopOffset;
private boolean mShowAtTop;
+ @IdRes
+ private int mIgnoreOffsetTopLimitViewId = ID_NULL;
private boolean mIsDragging;
private boolean mOpenOnClick;
@@ -156,6 +161,10 @@ public class ResolverDrawerLayout extends ViewGroup {
mIsMaxCollapsedHeightSmallExplicit =
a.hasValue(R.styleable.ResolverDrawerLayout_maxCollapsedHeightSmall);
mShowAtTop = a.getBoolean(R.styleable.ResolverDrawerLayout_showAtTop, false);
+ if (a.hasValue(R.styleable.ResolverDrawerLayout_ignoreOffsetTopLimit)) {
+ mIgnoreOffsetTopLimitViewId = a.getResourceId(
+ R.styleable.ResolverDrawerLayout_ignoreOffsetTopLimit, ID_NULL);
+ }
a.recycle();
mScrollIndicatorDrawable = mContext.getDrawable(R.drawable.scroll_indicator_material);
@@ -577,12 +586,32 @@ public class ResolverDrawerLayout extends ViewGroup {
dy -= 1.0f;
}
+ boolean isIgnoreOffsetLimitSet = false;
+ int ignoreOffsetLimit = 0;
+ View ignoreOffsetLimitView = findIgnoreOffsetLimitView();
+ if (ignoreOffsetLimitView != null) {
+ LayoutParams lp = (LayoutParams) ignoreOffsetLimitView.getLayoutParams();
+ ignoreOffsetLimit = ignoreOffsetLimitView.getBottom() + lp.bottomMargin;
+ isIgnoreOffsetLimitSet = true;
+ }
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
+ if (child.getVisibility() == View.GONE) {
+ continue;
+ }
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.ignoreOffset) {
child.offsetTopAndBottom((int) dy);
+ } else if (isIgnoreOffsetLimitSet) {
+ int top = child.getTop();
+ int targetTop = Math.max(
+ (int) (ignoreOffsetLimit + lp.topMargin + dy),
+ lp.mFixedTop);
+ if (top != targetTop) {
+ child.offsetTopAndBottom(targetTop - top);
+ }
+ ignoreOffsetLimit = child.getBottom() + lp.bottomMargin;
}
}
final boolean isCollapsedOld = mCollapseOffset != 0;
@@ -1024,6 +1053,8 @@ public class ResolverDrawerLayout extends ViewGroup {
final int rightEdge = width - getPaddingRight();
final int widthAvailable = rightEdge - leftEdge;
+ boolean isIgnoreOffsetLimitSet = false;
+ int ignoreOffsetLimit = 0;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
@@ -1036,9 +1067,24 @@ public class ResolverDrawerLayout extends ViewGroup {
continue;
}
+ if (mIgnoreOffsetTopLimitViewId != ID_NULL && !isIgnoreOffsetLimitSet) {
+ if (mIgnoreOffsetTopLimitViewId == child.getId()) {
+ ignoreOffsetLimit = child.getBottom() + lp.bottomMargin;
+ isIgnoreOffsetLimitSet = true;
+ }
+ }
+
int top = ypos + lp.topMargin;
if (lp.ignoreOffset) {
- top -= mCollapseOffset;
+ if (!isDragging()) {
+ lp.mFixedTop = (int) (top - mCollapseOffset);
+ }
+ if (isIgnoreOffsetLimitSet) {
+ top = Math.max(ignoreOffsetLimit + lp.topMargin, (int) (top - mCollapseOffset));
+ ignoreOffsetLimit = top + child.getMeasuredHeight() + lp.bottomMargin;
+ } else {
+ top -= mCollapseOffset;
+ }
}
final int bottom = top + child.getMeasuredHeight();
@@ -1102,11 +1148,23 @@ public class ResolverDrawerLayout extends ViewGroup {
mCollapsibleHeightReserved = ss.mCollapsibleHeightReserved;
}
+ private View findIgnoreOffsetLimitView() {
+ if (mIgnoreOffsetTopLimitViewId == ID_NULL) {
+ return null;
+ }
+ View v = findViewById(mIgnoreOffsetTopLimitViewId);
+ if (v != null && v != this && v.getParent() == this && v.getVisibility() != View.GONE) {
+ return v;
+ }
+ return null;
+ }
+
public static class LayoutParams extends MarginLayoutParams {
public boolean alwaysShow;
public boolean ignoreOffset;
public boolean hasNestedScrollIndicator;
public int maxHeight;
+ int mFixedTop;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 68db603c3671..422bdc98e9b5 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1530,12 +1530,14 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_os_VintfRuntimeInfo),
REG_JNI(register_android_service_DataLoaderService),
REG_JNI(register_android_view_DisplayEventReceiver),
- REG_JNI(register_android_view_InputApplicationHandle),
- REG_JNI(register_android_view_InputWindowHandle),
REG_JNI(register_android_view_Surface),
REG_JNI(register_android_view_SurfaceControl),
REG_JNI(register_android_view_SurfaceControlHdrLayerInfoListener),
REG_JNI(register_android_view_SurfaceSession),
+ REG_JNI(register_android_view_InputApplicationHandle),
+ // This must be called after register_android_view_SurfaceControl since it has a dependency
+ // on the Java SurfaceControl object that references a native resource via static request.
+ REG_JNI(register_android_view_InputWindowHandle),
REG_JNI(register_android_view_CompositionSamplingListener),
REG_JNI(register_android_view_TextureView),
REG_JNI(register_android_view_TunnelModeEnabledListener),
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index bb4ab39a59d1..1f64df49cb56 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -101,9 +101,18 @@ static void android_os_Parcel_markSensitive(jlong nativePtr)
static void android_os_Parcel_markForBinder(JNIEnv* env, jclass clazz, jlong nativePtr,
jobject binder)
{
+ LOG_ALWAYS_FATAL_IF(binder == nullptr, "Null binder specified for markForBinder");
+
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel) {
- parcel->markForBinder(ibinderForJavaObject(env, binder));
+ sp<IBinder> nBinder = ibinderForJavaObject(env, binder);
+
+ if (nBinder == nullptr) {
+ ALOGE("Native binder in markForBinder is null for non-null jobject");
+ return;
+ }
+
+ parcel->markForBinder(nBinder);
}
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index f28e2f636dac..9f88f3369ae8 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -798,6 +798,12 @@ sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
JavaBBinderHolder* jbh = (JavaBBinderHolder*)
env->GetLongField(obj, gBinderOffsets.mObject);
+
+ if (jbh == nullptr) {
+ ALOGE("JavaBBinderHolder null on binder");
+ return nullptr;
+ }
+
return jbh->get(env, obj);
}
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index aece8c35a9e6..39ec0374dc5e 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -57,9 +57,6 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
const InputDeviceIdentifier& ident = deviceInfo.getIdentifier();
- // Not sure why, but JNI is complaining when I pass this through directly.
- jboolean hasMic = deviceInfo.hasMic() ? JNI_TRUE : JNI_FALSE;
-
ScopedLocalRef<jobject>
inputDeviceObj(env,
env->NewObject(gInputDeviceClassInfo.clazz, gInputDeviceClassInfo.ctor,
@@ -70,8 +67,9 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
deviceInfo.isExternal(), deviceInfo.getSources(),
deviceInfo.getKeyboardType(), kcmObj.get(),
deviceInfo.getCountryCode(), deviceInfo.hasVibrator(),
- hasMic, deviceInfo.hasButtonUnderPad(),
- deviceInfo.hasSensor(), deviceInfo.hasBattery()));
+ deviceInfo.hasMic(), deviceInfo.hasButtonUnderPad(),
+ deviceInfo.hasSensor(), deviceInfo.hasBattery(),
+ deviceInfo.supportsUsi()));
const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
for (const InputDeviceInfo::MotionRange& range: ranges) {
@@ -94,7 +92,7 @@ int register_android_view_InputDevice(JNIEnv* env)
gInputDeviceClassInfo.ctor =
GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
"(IIILjava/lang/String;IILjava/lang/"
- "String;ZIILandroid/view/KeyCharacterMap;IZZZZZ)V");
+ "String;ZIILandroid/view/KeyCharacterMap;IZZZZZZ)V");
gInputDeviceClassInfo.addMotionRange = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz,
"addMotionRange", "(IIFFFFF)V");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b11f22a030d9..e36815c4beb9 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -368,11 +368,14 @@ static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
return reinterpret_cast<jlong>(surface.get());
}
-static void nativeRelease(JNIEnv* env, jclass clazz, jlong nativeObject) {
- sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(nativeObject));
+static void release(SurfaceControl* ctrl) {
ctrl->decStrong((void *)nativeCreate);
}
+static jlong nativeGetNativeSurfaceControlFinalizer(JNIEnv* env, jclass clazz) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&release));
+}
+
static void nativeDisconnect(JNIEnv* env, jclass clazz, jlong nativeObject) {
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
if (ctrl != NULL) {
@@ -886,34 +889,6 @@ static void nativeSetDestinationFrame(JNIEnv* env, jclass clazz, jlong transacti
transaction->setDestinationFrame(ctrl, crop);
}
-static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
- const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
- jlongArray array = env->NewLongArray(displayIds.size());
- if (array == nullptr) {
- jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
- return nullptr;
- }
-
- if (displayIds.empty()) {
- return array;
- }
-
- jlong* values = env->GetLongArrayElements(array, 0);
- for (size_t i = 0; i < displayIds.size(); ++i) {
- values[i] = static_cast<jlong>(displayIds[i].value);
- }
-
- env->ReleaseLongArrayElements(array, values, 0);
- return array;
-}
-
-static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
- const auto id = DisplayId::fromValue<PhysicalDisplayId>(physicalDisplayId);
- if (!id) return nullptr;
- sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(*id);
- return javaObjectForIBinder(env, token);
-}
-
static jobject nativeGetDisplayedContentSamplingAttributes(JNIEnv* env, jclass clazz,
jobject tokenObj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
@@ -1925,8 +1900,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeCopyFromSurfaceControl },
{"nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
(void*)nativeWriteToParcel },
- {"nativeRelease", "(J)V",
- (void*)nativeRelease },
+ {"nativeGetNativeSurfaceControlFinalizer", "()J",
+ (void*) nativeGetNativeSurfaceControlFinalizer },
{"nativeDisconnect", "(J)V",
(void*)nativeDisconnect },
{"nativeUpdateDefaultBufferSize", "(JII)V",
@@ -1992,10 +1967,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetFrameRate },
{"nativeSetDefaultFrameRateCompatibility", "(JJI)V",
(void*)nativeSetDefaultFrameRateCompatibility},
- {"nativeGetPhysicalDisplayIds", "()[J",
- (void*)nativeGetPhysicalDisplayIds },
- {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
- (void*)nativeGetPhysicalDisplayToken },
{"nativeSetDisplaySurface", "(JLandroid/os/IBinder;J)V",
(void*)nativeSetDisplaySurface },
{"nativeSetDisplayLayerStack", "(JLandroid/os/IBinder;I)V",
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 6a200d05c2d7..a06e8a4ea4a0 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -23,6 +23,7 @@
android:maxWidth="@dimen/resolver_max_width"
android:maxCollapsedHeight="@dimen/resolver_max_collapsed_height"
android:maxCollapsedHeightSmall="56dp"
+ android:ignoreOffsetTopLimit="@id/title_container"
android:id="@id/contentPanel">
<RelativeLayout
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 21643c9b96ca..4b9abad0f5a7 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Jy kan nie op jou <xliff:g id="DEVICE">%1$s</xliff:g> toegang hiertoe kry nie. Probeer eerder op jou Android TV-toestel."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Jy kan nie op jou <xliff:g id="DEVICE">%1$s</xliff:g> toegang hiertoe kry nie. Probeer eerder op jou tablet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Jy kan nie op jou <xliff:g id="DEVICE">%1$s</xliff:g> toegang hiertoe kry nie. Probeer eerder op jou foon."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Hierdie program is vir \'n ouer weergawe van Android gebou en sal dalk nie behoorlik werk nie. Probeer kyk vir opdaterings, of kontak die ontwikkelaar."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Hierdie program is vir ’n ouer weergawe van Android gebou. Dit sal dalk nie behoorlik werk nie en dit sluit nie die jongste sekuriteit en privaatheidbeskermings in nie. Kyk of daar ’n opdatering is of kontak die program se ontwikkelaar."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Kyk vir opdatering"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Jy het nuwe boodskappe"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Maak SMS-program oop om te bekyk"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index af1cdad7820a..ebd536d954f9 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"ይህ በእርስዎ <xliff:g id="DEVICE">%1$s</xliff:g> ላይ ሊደረስበት አይችልም። በምትኩ በAndroid TV መሣሪያዎ ላይ ይሞክሩ።"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"ይህ በእርስዎ <xliff:g id="DEVICE">%1$s</xliff:g> ላይ ሊደረስበት አይችልም። በምትኩ በጡባዊዎ ላይ ይሞክሩ።"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"ይህ በእርስዎ <xliff:g id="DEVICE">%1$s</xliff:g> ላይ ሊደረስበት አይችልም። በምትኩ በስልክዎ ላይ ይሞክሩ።"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ይህ መተግበሪያ ለቆየ የAndroid ስሪት ነው የተገነባው፣ እና በአግባቡ ላይሰራ ይችላል። ዝማኔዎች ካሉ ለመመልከት ይሞክሩ፣ ወይም ደግሞ ገንቢውን ያነጋግሩ።"</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ዝማኔ ካለ አረጋግጥ"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"አዲስ መልዕክቶች አለዎት"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"ለመመልከት የኤስኤምኤስ መተግበሪያ ይክፈቱ"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"የአንድ ጊዜ መዳረሻን ፍቀድ"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"አትፍቀድ"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"የመሣሪያ ምዝግብ ማስታወሻዎች በመሣሪያዎ ላይ ምን እንደሚከሰት ይመዘግባሉ። መተግበሪያዎች ችግሮችን ለማግኘት እና ለማስተካከል እነዚህን ምዝግብ ማስታወሻዎች መጠቀም ይችላሉ።\n\nአንዳንድ ምዝግብ ማስታወሻዎች ሚስጥራዊነት ያለው መረጃ ሊይዙ ይችላሉ፣ ስለዚህ የሚያምኗቸውን መተግበሪያዎች ብቻ ሁሉንም የመሣሪያ ምዝግብ ማስታወሻዎች እንዲደርሱ ይፍቀዱላቸው። \n\nይህ መተግበሪያ ሁሉንም የመሣሪያ ምዝግብ ማስታወሻዎች እንዲደርስ ካልፈቀዱለት አሁንም የራሱን ምዝግብ ማስታወሻዎች መድረስ ይችላል። የእርስዎ መሣሪያ አምራች አሁንም አንዳንድ ምዝግብ ማስታወሻዎችን ወይም መረጃዎችን በመሣሪያዎ ላይ ሊደርስ ይችላል።"</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"የመሣሪያ ምዝግብ ማስታወሻዎች በመሣሪያዎ ላይ ምን እንደሚከሰት ይመዘግባሉ። መተግበሪያዎች ችግሮችን ለማግኘት እና ለማስተካከል እነዚህን ምዝግብ ማስታወሻዎች መጠቀም ይችላሉ።\n\nአንዳንድ ምዝግብ ማስታወሻዎች ሚስጥራዊነት ያለው መረጃ ሊይዙ ይችላሉ፣ ስለዚህ የሚያምኗቸው መተግበሪያዎች ብቻ ሁሉንም የመሣሪያ ምዝግብ ማስታወሻዎች እንዲደርሱ ይፍቀዱላቸው። \n\nይህ መተግበሪያ ሁሉንም የመሣሪያ ምዝግብ ማስታወሻዎች እንዲደርስ ካልፈቀዱለት አሁንም የራሱን ምዝግብ ማስታወሻዎች መድረስ ይችላል። የመሣሪያዎ አምራች አሁንም አንዳንድ ምዝግብ ማስታወሻዎችን ወይም መረጃዎችን በመሣሪያዎ ላይ ሊደርስ ይችላል።\n\ng.co/android/devicelogs ላይ የበለጠ ይወቁ።"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"ዳግም አታሳይ"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> የ<xliff:g id="APP_2">%2$s</xliff:g> ቁራጮችን ማሳየት ይፈልጋል"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"አርትዕ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 81b73ca28936..d009d50bdd6b 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -509,7 +509,7 @@
<string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"للسماح للتطبيق بالحصول على قائمة بالحسابات التي يعرفها الهاتف. وقد يتضمن ذلك أي حسابات تم إنشاؤها بواسطة التطبيقات التي ثبتها."</string>
<string name="permlab_accessNetworkState" msgid="2349126720783633918">"عرض اتصالات الشبكة"</string>
<string name="permdesc_accessNetworkState" msgid="4394564702881662849">"للسماح للتطبيق بعرض معلومات حول اتصالات الشبكة كعرض معلومات عن الشبكات المتوفرة والشبكات المتصلة."</string>
- <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"حق الوصول الكامل إلى الشبكة"</string>
+ <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"الإذن بالوصول الكامل إلى الشبكة"</string>
<string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"للسماح للتطبيق بإنشاء مقابس شبكات واستخدام بروتوكولات شبكات مخصصة. ويوفر المتصفح وتطبيقات أخرى طرقًا لإرسال البيانات إلى الإنترنت، ولذلك لا يعد هذا الإذن مطلوبًا لإرسال البيانات إلى الإنترنت."</string>
<string name="permlab_changeNetworkState" msgid="8945711637530425586">"تغيير اتصال الشبكة"</string>
<string name="permdesc_changeNetworkState" msgid="649341947816898736">"للسماح للتطبيق بتغيير حالة اتصال الشبكة."</string>
@@ -1270,7 +1270,7 @@
<string name="dump_heap_ready_notification" msgid="2302452262927390268">"نَسْخ الذاكرة <xliff:g id="PROC">%1$s</xliff:g> جاهز"</string>
<string name="dump_heap_notification_detail" msgid="8431586843001054050">"تم جمع مقدار كبير من بيانات الذاكرة. انقر للمشاركة."</string>
<string name="dump_heap_title" msgid="4367128917229233901">"هل تريد مشاركة نَسْخ الذاكرة؟"</string>
- <string name="dump_heap_text" msgid="1692649033835719336">"تجاوزت عملية <xliff:g id="PROC">%1$s</xliff:g> حد الذاكرة المخصص لها وقدره <xliff:g id="SIZE">%2$s</xliff:g>، ويتوفر نَسْخ للذاكرة لمشاركته مع مطور برامج العملية ولكن توخ الحذر حيث قد يحتوي نَسْخ الذاكرة هذا على معلومات شخصية يملك التطبيق حق الوصول إليها."</string>
+ <string name="dump_heap_text" msgid="1692649033835719336">"تجاوزت عملية <xliff:g id="PROC">%1$s</xliff:g> حد الذاكرة المخصص لها وقدره <xliff:g id="SIZE">%2$s</xliff:g>، ويتوفر نَسْخ للذاكرة لمشاركته مع مطور برامج العملية ولكن توخ الحذر حيث قد يحتوي نَسْخ الذاكرة هذا على معلومات شخصية يملك التطبيق الإذن بالوصول إليها."</string>
<string name="dump_heap_system_text" msgid="6805155514925350849">"تجاوزت عملية <xliff:g id="PROC">%1$s</xliff:g> القيد المفروض على الذاكرة الذي يبلغ <xliff:g id="SIZE">%2$s</xliff:g>. ويتوفّر نَسْخ ذاكرة يمكنك مشاركته. تحذير: قد يحتوي نَسْخ الذاكرة هذا على معلومات شخصية حسّاسة يمكن للعملية الوصول إليها، وقد يتضمن معلومات سبق لك كتابتها."</string>
<string name="dump_heap_ready_text" msgid="5849618132123045516">"يتوفّر نَسْخ ذاكرة من عملية <xliff:g id="PROC">%1$s</xliff:g> حتى تتمكّن من مشاركته. تحذير: قد يحتوي نَسْخ الذاكرة هذا على معلومات شخصية حسّاسة يمكن للعملية الوصول إليها، وقد يتضمن معلومات سبق لك كتابتها."</string>
<string name="sendText" msgid="493003724401350724">"اختيار إجراء للنص"</string>
@@ -1962,7 +1962,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"‏لا يمكن الوصول إلى هذه الإعدادات على <xliff:g id="DEVICE">%1$s</xliff:g>. بدلاً من ذلك، جرِّب استخدام جهاز Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"لا يمكن الوصول إلى هذه الإعدادات على <xliff:g id="DEVICE">%1$s</xliff:g>. بدلاً من ذلك، جرِّب استخدام جهازك اللوحي."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"لا يمكن الوصول إلى هذه الإعدادات على <xliff:g id="DEVICE">%1$s</xliff:g>. بدلاً من ذلك، جرِّب استخدام هاتفك."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏تمّ إنشاء هذا التطبيق لإصدار قديم من Android وقد لا يعمل بشكل صحيح. جرِّب البحث عن تحديثات أو الاتصال بمطوّر البرامج."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"البحث عن تحديث"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"لديك رسائل جديدة"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"‏فتح تطبيق الرسائل القصيرة SMS للعرض"</string>
@@ -2055,8 +2056,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"السماح بالوصول إلى السجلّ لمرة واحدة"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"عدم السماح"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"ترصد سجلّات الجهاز ما يحدث على جهازك. يمكن أن تستخدم التطبيقات هذه السجلّات لتحديد المشاكل وحلها.\n\nقد تحتوي بعض السجلّات على معلومات حساسة، ولذلك يجب عدم السماح بالوصول إلى جميع سجلّات الجهاز إلا للتطبيقات التي تثق بها. \n\nإذا لم تسمح بوصول هذا التطبيق إلى جميع سجلّات الجهاز، يظل بإمكان التطبيق الوصول إلى سجلّاته. ويظل بإمكان الشركة المصنِّعة لجهازك الوصول إلى بعض السجلّات أو المعلومات المتوفّرة على جهازك."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"‏ترصد سجلّات الجهاز ما يحدث على جهازك. يمكن أن تستخدم التطبيقات هذه السجلّات لتحديد المشاكل وحلّها.\n\nقد تحتوي بعض السجلّات على معلومات حساسة، ولذلك يجب عدم السماح بالوصول إلى جميع سجلّات الجهاز إلا للتطبيقات التي تثق بها. \n\nإذا لم تسمح بوصول هذا التطبيق إلى جميع سجلّات الجهاز، يظل بإمكان التطبيق الوصول إلى سجلّاته. وقد يظل بإمكان الشركة المصنِّعة لجهازك الوصول إلى بعض السجلّات أو المعلومات المتوفّرة على جهازك.\n\nتعرَّف على مزيد من المعلومات على الرابط g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"عدم الإظهار مرة أخرى"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"يريد تطبيق <xliff:g id="APP_0">%1$s</xliff:g> عرض شرائح تطبيق <xliff:g id="APP_2">%2$s</xliff:g>."</string>
<string name="screenshot_edit" msgid="7408934887203689207">"تعديل"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 192d07a36a4c..3b6eef175c0b 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -432,8 +432,8 @@
<string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"এই এপটোৱে আপোনাৰ টেবলেটত কেলেণ্ডাৰ কাৰ্যক্ৰম যোগ কৰিব, আঁতৰাব বা সলনি কৰিব পাৰে। ই এনে বাৰ্তা পঠিয়াব পাৰে যিবোৰ কেলেণ্ডাৰৰ গৰাকীৰ পৰা অহা যেন লাগিব বা ই গৰাকীক নজনোৱাকৈ কাৰ্যক্ৰম সলনি কৰিব পাৰে৷"</string>
<string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"এই এপ্‌টোৱে আপোনাৰ Android TV ডিভাইচত কেলেণ্ডাৰ অনুষ্ঠানবোৰ যোগ দিব, আঁতৰাব অথবা সলনি কৰিব পাৰে। এই এপ্‌টোৱে এনে বাৰ্তা পঠিয়াব পাৰে যিবোৰ কেলেণ্ডাৰৰ গৰাকীৰ পৰা অহা বুলি প্ৰদর্শিত হ’ব পাৰে অথবা এইটোৱে গৰাকীসকলক নজনোৱাকৈ অনুষ্ঠানবোৰ সলনি কৰিব পাৰে।"</string>
<string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"এই এপে আপোনাৰ ফ\'নৰ কেলেণ্ডাৰত কার্যক্ৰম যোগ দিব, আঁতৰাব বা সলনি কৰিব পাৰে। ই এনে বাৰ্তা পঠিয়াব পাৰে যিবোৰ কেলেণ্ডাৰৰ গৰাকীৰ পৰা অহা যেন লাগে বা ই গৰাকীক নজনোৱাকৈ কাৰ্যক্ৰম সলনি কৰিব পাৰে৷"</string>
- <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"অতিৰিক্ত অৱস্থান দেখুওৱা নির্দেশত প্ৰৱেশ কৰক"</string>
- <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"অৱস্থানৰ অতিৰিক্ত নির্দেশনাসমূহত প্ৰৱেশ কৰিবলৈ এপক অনুমতি দিয়ে। ইয়ে এপটোক জিপিএছ বা অন্য অৱস্থান উৎসসমূহৰ কাৰ্যকলাপত হস্তক্ষেপ কৰাৰ সুযোগ দিব পাৰে।"</string>
+ <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"অতিৰিক্ত অৱস্থান প্ৰদানকাৰী নির্দেশসমূহ এক্সেছ কৰে"</string>
+ <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"অৱস্থানৰ অতিৰিক্ত নির্দেশনাসমূহ এক্সেছ কৰিবলৈ এপক অনুমতি দিয়ে। ই এপ্‌টোক জিপিএছ বা অন্য অৱস্থান উৎসসমূহৰ কাৰ্যকলাপত হস্তক্ষেপ কৰাৰ সুযোগ দিব পাৰে।"</string>
<string name="permlab_accessFineLocation" msgid="6426318438195622966">"কেৱল অগ্ৰভূমিত অৱস্থানৰ সঠিক তথ্য পাওক"</string>
<string name="permdesc_accessFineLocation" msgid="6732174080240016335">"এই এপ্‌টো ব্যৱহাৰ হৈ থকা অৱস্থাত ই অৱস্থান সেৱাসমূহৰ পৰা আপোনাৰ সঠিক অৱস্থান লাভ কৰিব পাৰে। এপ্‌টোৱে অৱস্থান লাভ কৰিবলৈ হ’লে আপোনাৰ ডিভাইচৰ অৱস্থান সেৱাসমূহ অন কৰি ৰাখিবই লাগিব। ইয়াৰ ফলত বেটাৰীৰ ব্যৱহাৰ বাঢ়িব পাৰে।"</string>
<string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"কেৱল অগ্ৰভূমিত আনুমানিক অৱস্থান এক্সেছ কৰক"</string>
@@ -719,7 +719,7 @@
<string name="permdesc_manageNetworkPolicy" msgid="1865663268764673296">"এপটোক নেটৱৰ্ক সংযোগৰ নীতিসমূহ পৰিচালনা কৰিবলৈ আৰু এপ্-বিশেষ নিয়ম সংজ্ঞাবদ্ধ কৰিবলৈ অনুমতি দিয়ে।"</string>
<string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"নেটৱর্ক ব্যৱহাৰৰ হিচাপ সলনি কৰক"</string>
<string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"এপ অনুসুৰি নেটৱর্কৰ ব্যৱহাৰৰ হিচাপ সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। এয়া সাধাৰণ এপবোৰৰ ব্যৱহাৰৰ বাবে নহয়।"</string>
- <string name="permlab_accessNotifications" msgid="7130360248191984741">"প্ৰৱেশ জাননীসমূহ"</string>
+ <string name="permlab_accessNotifications" msgid="7130360248191984741">"জাননীসমূহ এক্সেছ কৰে"</string>
<string name="permdesc_accessNotifications" msgid="761730149268789668">"অন্য এপসমূহৰদ্বাৰা প\'ষ্ট কৰা জাননীসমূহকে ধৰি জাননী বিচাৰি উলিয়াবলৈ, পৰীক্ষা কৰিবলৈ আৰু মচিবলৈ অনুমতি দিয়ে।"</string>
<string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"এটা জাননী শুনা সেৱাৰ লগত সংযুক্ত হ\'ব পাৰে"</string>
<string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"এটা জাননী শ্ৰৱণ সেৱা প্ৰদানকাৰীৰ শীৰ্ষ স্তৰৰ ইণ্টাৰফেইচৰ লগত সংযুক্ত হ\'বলৈ ধাৰকক অনুমতি দিয়ে। সাধাৰণ এপসমূহৰ বাবে সাধাৰণতে প্ৰয়োজন নহয়।"</string>
@@ -733,7 +733,7 @@
<string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"এটা এপ্লিকেশ্বনক নেটৱৰ্ক অৱস্থাসমূহত নিৰীক্ষণৰ বাবে শুনিবলৈ অনুমতি দিয়ে। সাধাৰণ এপসমূহৰ বাবে সাধাৰণতে প্ৰয়োজন নহয়।"</string>
<string name="permlab_setInputCalibration" msgid="932069700285223434">"ইনপুট ডিভাইচ কেলিব্ৰেশ্বন সলনি কৰিব পাৰে"</string>
<string name="permdesc_setInputCalibration" msgid="2937872391426631726">"টাচ্চ স্ক্ৰীনৰ কেলিব্ৰেশ্বন পেৰামিটাৰ সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। সাধাৰণ এপসমূহৰ বাবে কেতিয়াও প্ৰয়োজন হোৱা উচিত নহয়।"</string>
- <string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"DRM প্ৰমাণপত্ৰসমূহলৈ প্ৰৱেশ"</string>
+ <string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"DRM প্ৰমাণপত্ৰসমূহ এক্সেছ কৰে"</string>
<string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"এটা এপ্লিকেশ্বনক DRM প্ৰমাণপত্ৰ গোটাবলৈ আৰু ব্যৱহাৰ কৰিবলৈ অনুমতি দিয়ে। সাধাৰণ এপসমূহৰ বাবে সাধাৰণতে প্ৰয়োজন নহয়।"</string>
<string name="permlab_handoverStatus" msgid="7620438488137057281">"Android বীম স্থানান্তৰণৰ স্থিতি লাভ কৰিব পাৰে"</string>
<string name="permdesc_handoverStatus" msgid="3842269451732571070">"বৰ্তমানৰ Android Beam স্থানান্তৰণসমূহৰ বিষয়ে তথ্য পাবলৈ এই এপ্লিকেশ্বনক অনুমতি দিয়ে"</string>
@@ -1468,7 +1468,7 @@
<string name="ime_action_default" msgid="8265027027659800121">"কার্য কৰক"</string>
<string name="dial_number_using" msgid="6060769078933953531">"<xliff:g id="NUMBER">%s</xliff:g> ব্যৱহাৰ কৰি \n নম্বৰটো ডায়েল কৰক"</string>
<string name="create_contact_using" msgid="6200708808003692594">"<xliff:g id="NUMBER">%s</xliff:g> ব্যৱহাৰ কৰি সম্পৰ্ক \n সৃষ্টি কৰক"</string>
- <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"বৰ্তমান আৰু ভৱিষ্যতে আপোনাৰ একাউণ্টত প্ৰৱেশ কৰিবলৈ তলৰ এটা বা অধিক এপে অনুমতি লাভৰ বাবে অনুৰোধ কৰিছে৷"</string>
+ <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"তলৰ এটা বা একাধিক এপে বৰ্তমান আৰু ভৱিষ্যতে আপোনাৰ একাউণ্ট এক্সেছ কৰিবলৈ অনুমতি বিচাৰি অনুৰোধ কৰিছে৷"</string>
<string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"আপুনি এই অনুৰোধক সন্মতি দিব বিচাৰেনে?"</string>
<string name="grant_permissions_header_text" msgid="3420736827804657201">"ব্যৱহাৰ কৰাৰ অনুমতি বিচাৰি কৰা অনুৰোধ"</string>
<string name="allow" msgid="6195617008611933762">"অনুমতি দিয়ক"</string>
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"এইটো আপোনাৰ <xliff:g id="DEVICE">%1$s</xliff:g>ত এক্সেছ কৰিব নোৱাৰি। তাৰ পৰিৱৰ্তে আপোনাৰ Android TV ডিভাইচত চেষ্টা কৰি চাওক।"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"এইটো আপোনাৰ <xliff:g id="DEVICE">%1$s</xliff:g>ত এক্সেছ কৰিব নোৱাৰি। তাৰ পৰিৱৰ্তে আপোনাৰ টেবলেটত চেষ্টা কৰি চাওক।"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"এইটো আপোনাৰ <xliff:g id="DEVICE">%1$s</xliff:g>ত এক্সেছ কৰিব নোৱাৰি। তাৰ পৰিৱৰ্তে আপোনাৰ ফ’নত চেষ্টা কৰি চাওক।"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"এই এপটো Androidৰ এটা পুৰণা সংস্কৰণৰ বাবে প্ৰস্তুত কৰা হৈছিল, আৰু ই বিচৰাধৰণে কাম নকৰিবও পাৰে। ইয়াৰ আপডে’ট আছে নেকি চাওক, বা বিকাশকৰ্তাৰ সৈতে যোগাযোগ কৰক।"</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"আপডে’ট আছে নেকি চাওক"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"আপুনি নতুন বার্তা লাভ কৰিছে"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"চাবলৈ এছএমএছ এপ্ খোলক"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"কেৱল এবাৰ এক্সেছ কৰাৰ অনুমতি দিয়ক"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"অনুমতি নিদিব"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"আপোনাৰ ডিভাইচত কি কি ঘটে সেয়া ডিভাইচ লগে ৰেকৰ্ড কৰে। এপ্‌সমূহে সমস্যা বিচাৰিবলৈ আৰু সমাধান কৰিবলৈ এই লগসমূহ ব্যৱহাৰ কৰিব পাৰে।\n\nকিছুমান লগত সংবেদনশীল তথ্য থাকিব পাৰে, গতিকে কেৱল আপুনি বিশ্বাস কৰা এপকহে আটাইবোৰ ডিভাইচ লগ এক্সেছ কৰাৰ অনুমতি দিয়ক। \n\nআপুনি যদি এই এপ্‌টোক আটাইবোৰ ডিভাইচ লগ এক্সেছ কৰাৰ অনুমতি নিদিয়ে, তথাপিও ই নিজৰ লগসমূহ এক্সেছ কৰিব পাৰিব। আপোনাৰ ডিভাইচৰ নিৰ্মাতাই তথাপিও হয়তো আপোনাৰ ডিভাইচটোত থকা কিছু লগ অথবা তথ্য এক্সেছ কৰিব পাৰিব।"</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"আপোনাৰ ডিভাইচত কি কি ঘটে সেয়া ডিভাইচ লগে ৰেকৰ্ড কৰে। এপ্‌সমূহে সমস্যা বিচাৰিবলৈ আৰু সমাধান কৰিবলৈ এই লগসমূহ ব্যৱহাৰ কৰিব পাৰে।\n\nকিছুমান লগত সংবেদনশীল তথ্য থাকিব পাৰে, গতিকে কেৱল আপুনি বিশ্বাস কৰা এপকহে আটাইবোৰ ডিভাইচ লগ এক্সেছ কৰাৰ অনুমতি দিয়ক। \n\nআপুনি যদি এই এপ্‌টোক আটাইবোৰ ডিভাইচ লগ এক্সেছ কৰাৰ অনুমতি নিদিয়ে, তথাপিও ই নিজৰ লগসমূহ এক্সেছ কৰিব পাৰিব। আপোনাৰ ডিভাইচৰ নিৰ্মাতাই তথাপিও হয়তো আপোনাৰ ডিভাইচটোত থকা কিছু লগ অথবা তথ্য এক্সেছ কৰিব পাৰিব।\n\ng.co/android/devicelogsত অধিক জানক।"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"পুনৰ নেদেখুৱাব"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>এ <xliff:g id="APP_2">%2$s</xliff:g>ৰ অংশ দেখুওৱাব খুজিছে"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"সম্পাদনা কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 29124f220ca0..586adeff170f 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızda buna giriş mümkün deyil. Android TV cihazınızda sınayın."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızda buna giriş mümkün deyil. Planşetinizdə sınayın."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızda buna giriş mümkün deyil. Telefonunuzda sınayın."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Bu tətbiq köhnə Android versiyası üçün hazırlanıb və düzgün işləməyə bilər. Güncəlləməni yoxlayın və ya developer ilə əlaqə saxlayın."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Bu tətbiq köhnə Android versiyası üçün nəzərdə tutulub. O, düzgün işləməyə bilər və ən son təhlükəsizlik və məxfilik qorumalarını ehtiva etmir. Güncəlləməni yoxlayın və ya tətbiq tərtibatçısı ilə əlaqə saxlayın."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Güncəllənmə olmasını yoxlayın"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Yeni mesajlarınız var"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Baxmaq üçün SMS tətbiqini açın"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 6b5d8d207e6f..c19338d5bd8b 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1959,7 +1959,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Ovoj aplikaciji ne može da se pristupi sa uređaja <xliff:g id="DEVICE">%1$s</xliff:g>. Probajte na Android TV uređaju."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Ovoj aplikaciji ne može da se pristupi sa uređaja <xliff:g id="DEVICE">%1$s</xliff:g>. Probajte na tabletu."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ovoj aplikaciji ne može da se pristupi sa uređaja <xliff:g id="DEVICE">%1$s</xliff:g>. Probajte na telefonu."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ova aplikacija je napravljena za stariju verziju Android-a, pa možda neće raditi ispravno. Potražite ažuriranja ili kontaktirajte programera."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ova aplikacija je napravljena za stariju verziju Android-a. Možda neće raditi ispravno i ne obuhvata najnovije bezbednosne funkcije i zaštite privatnosti. Proverite da li ima ažuriranja ili se obratite programeru aplikacije."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Potraži ažuriranje"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nove poruke"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Otvorite aplikaciju za SMS da biste pregledali"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 8a1463704d75..ab338c7fe9ca 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1960,7 +1960,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Не ўдаецца атрымаць доступ з прылады \"<xliff:g id="DEVICE">%1$s</xliff:g>\". Паспрабуйце скарыстаць прыладу Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Не ўдаецца атрымаць доступ з прылады \"<xliff:g id="DEVICE">%1$s</xliff:g>\". Паспрабуйце скарыстаць планшэт."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Не ўдаецца атрымаць доступ з прылады \"<xliff:g id="DEVICE">%1$s</xliff:g>\". Паспрабуйце скарыстаць тэлефон."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Гэта праграма была створана для больш старой версіі Android і можа не працаваць належным чынам. Праверце наяўнасць абнаўленняў або звярніцеся да распрацоўшчыка."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Гэта праграма створана для ранейшай версіі Android. Яна можа кепска працаваць, і ў ёй няма новых сродкаў абароны бяспекі і прыватнасці. Праверце наяўнасць абнаўленняў ці звярніцеся да распрацоўшчыка праграмы."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Праверыць наяўнасць абнаўленняў"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"У вас ёсць новыя паведамленні"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Праглядзець праз праграму для SMS"</string>
@@ -2053,8 +2053,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Дазволіць аднаразовы доступ"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Не дазваляць"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Журналы прылад запісваюць усё, што адбываецца на вашай прыладзе. Праграмы выкарыстоўваюць гэтыя журналы для пошуку і выпраўлення памылак.\n\nУ некаторых журналах можа ўтрымлівацца канфідэнцыяльная інфармацыя, таму давайце доступ да ўсіх журналаў прылады толькі тым праграмам, якім вы давяраеце. \n\nКалі вы не дасце гэтай праграме доступу да ўсіх журналаў прылад, у яе ўсё роўна застанецца доступ да ўласных журналаў. Для вытворцы вашай прылады будуць даступнымі некаторыя журналы і інфармацыя на вашай прыладзе."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Журналы прылад запісваюць усё, што адбываецца на вашай прыладзе. Праграмы выкарыстоўваюць гэтыя журналы для пошуку і выпраўлення памылак.\n\nУ некаторых журналах можа ўтрымлівацца канфідэнцыяльная інфармацыя, таму давайце доступ да ўсіх журналаў прылады толькі тым праграмам, якім вы давяраеце. \n\nКалі вы не дасце гэтай праграме доступу да ўсіх журналаў прылад, у яе ўсё роўна застанецца доступ да ўласных журналаў. Для вытворцы вашай прылады будуць даступнымі некаторыя журналы і інфармацыя на вашай прыладзе.\n\nДаведайцеся больш на старонцы g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Больш не паказваць"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Праграма <xliff:g id="APP_0">%1$s</xliff:g> запытвае дазвол на паказ зрэзаў праграмы <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Рэдагаваць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 901832491599..9c385e257075 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Не може да се осъществи достъп от устройството ви <xliff:g id="DEVICE">%1$s</xliff:g>. Вместо това опитайте от устройството си с Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Не може да се осъществи достъп от устройството ви <xliff:g id="DEVICE">%1$s</xliff:g>. Вместо това опитайте от таблета си."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Не може да се осъществи достъп от устройството ви <xliff:g id="DEVICE">%1$s</xliff:g>. Вместо това опитайте от телефона си."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Това приложение бе създадено за по-стара версия на Android и може да не работи правилно. Опитайте да проверите за актуализации или се свържете с програмиста."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Проверка за актуализация"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Имате нови съобщения"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Преглед в приложението за SMS"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Разрешаване на еднократен достъп"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Забраняване"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"В регистрационните файлове за устройството се записва какво се извършва на него. Приложенията могат да използват тези регистрационни файлове, за да откриват и отстраняват проблеми.\n\nНякои регистрационни файлове за устройството може да съдържат поверителна информация, затова разрешавайте достъп до всички тях само на приложения, на които имате доверие. \n\nАко не разрешите на това приложение достъп до всички регистрационни файлове за устройството, то пак може да осъществява достъп до собствените си регистрационни файлове. Производителят на устройството пак може да има достъп до някои регистрационни файлове или информация на устройството ви."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"В регистрационните файлове за устройството се записва какво се извършва на него. Приложенията могат да използват тези регистрационни файлове, за да откриват и отстраняват проблеми.\n\nНякои регистрационни файлове за устройството може да съдържат поверителна информация, затова разрешавайте достъп до всички тях само на приложения, на които имате доверие. \n\nАко не разрешите на това приложение достъп до всички регистрационни файлове за устройството, то пак може да осъществява достъп до собствените си регистрационни файлове. Производителят на устройството може да има достъп до някои регистрационни файлове или информация на устройството ви.\n\nНаучете повече на адрес g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Да не се показва пак"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> иска да показва части от <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Редактиране"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 70b64fd36e6d..34e6f713d4ad 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"আপনার <xliff:g id="DEVICE">%1$s</xliff:g>-এ এটি অ্যাক্সেস করা যাবে না। পরিবর্তে আপনার Android TV ডিভাইসে ব্যবহার করে দেখুন।"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"আপনার <xliff:g id="DEVICE">%1$s</xliff:g>-এ এটি অ্যাক্সেস করা যাবে না। পরিবর্তে আপনার ট্যাবলেটে ব্যবহার করে দেখুন।"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"আপনার <xliff:g id="DEVICE">%1$s</xliff:g>-এ এটি অ্যাক্সেস করা যাবে না। পরিবর্তে আপনার ফোনে ব্যবহার করে দেখুন।"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"এই অ্যাপটি Android এর একটি পুরনো ভার্সনের জন্য তৈরি করা হয়েছিল, তাই এখানে সেটি ঠিকমতো কাজ নাও করতে পারে। আপডেট পাওয়া যাচ্ছে কিনা দেখুন বা ডেভেলপারের সাথে যোগাযোগ করুন।"</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"আপডেট পাওয়া যাচ্ছে কিনা দেখুন"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"আপনার নতুন মেসেজ আছে"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"দেখার জন্য SMS অ্যাপ্লিকেশান খুলুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 89b6b704c61a..081d8f2588ac 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1959,7 +1959,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Ne možete pristupiti ovoj aplikaciji na uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Umjesto toga pokušajte na uređaju Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Ne možete pristupiti ovoj aplikaciji na uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Umjesto toga pokušajte na tabletu."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ne možete pristupiti ovoj aplikaciji na uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Umjesto toga pokušajte na telefonu."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ova aplikacija je pravljena za stariju verziju Androida i možda neće ispravno raditi. Provjerite jesu li dostupna ažuriranja ili kontaktirajte programera."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ova aplikacija je izrađena za stariju verziju Androida. Možda neće pravilno funkcionirati i ne sadržava najnovije sigurnosne zaštite i zaštite privatnosti. Provjerite ima li ažuriranja ili se obratite programeru aplikacije."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Provjeri je li dostupno ažuriranje"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nove poruke"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Otvorite SMS aplikaciju da biste pregledali poruke"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 934ae2122518..21538e7d0002 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1948,17 +1948,18 @@
<string name="app_streaming_blocked_title_for_playstore_dialog" msgid="8149823099822897538">"Play Store no està disponible"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="tv" msgid="196994247017450357">"La configuració d\'Android TV no està disponible"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="tablet" msgid="8222710146267948647">"La configuració de la tauleta no està disponible"</string>
- <string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"La configuració del telèfon no està disponible"</string>
- <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"En aquests moments, no es pot accedir a aquesta aplicació al dispositiu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al dispositiu Android TV."</string>
- <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"En aquests moments, no es pot accedir a aquesta aplicació al dispositiu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho a la tauleta."</string>
- <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"En aquests moments, no es pot accedir a aquesta aplicació al dispositiu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al telèfon."</string>
+ <string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"Configuració del telèfon no disponible"</string>
+ <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"En aquests moments, No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al dispositiu Android TV."</string>
+ <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"En aquests moments, No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho a la tauleta."</string>
+ <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al telèfon."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Aquesta aplicació sol·licita seguretat addicional. Prova-ho al dispositiu Android TV."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Aquesta aplicació sol·licita seguretat addicional. Prova-ho a la tauleta."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Aquesta aplicació sol·licita seguretat addicional. Prova-ho al telèfon."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"No s\'hi pot accedir des del dispositiu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al dispositiu Android TV."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"No s\'hi pot accedir des del dispositiu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho a la tauleta."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"No s\'hi pot accedir des del dispositiu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al telèfon."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Aquesta aplicació es va crear per a una versió antiga d\'Android i pot ser que no funcioni correctament. Prova de cercar actualitzacions o contacta amb el desenvolupador."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al dispositiu Android TV."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho a la tauleta."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al telèfon."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Cerca actualitzacions"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Tens missatges nous"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Obre l\'aplicació d\'SMS per veure\'ls"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permet l\'accés únic"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"No permetis"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Els registres del dispositiu inclouen informació sobre tot allò que passa al teu dispositiu. Les aplicacions poden utilitzar aquests registres per detectar i corregir problemes.\n\nÉs possible que alguns registres continguin informació sensible; per això només has de donar-hi accés a les aplicacions de confiança. \n\nEncara que no permetis que aquesta aplicació pugui accedir a tots els registres del dispositiu, podrà accedir als seus propis registres. És possible que el fabricant del dispositiu també tingui accés a alguns registres o a informació del teu dispositiu."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Els registres del dispositiu inclouen informació sobre tot allò que passa al teu dispositiu. Les aplicacions poden utilitzar aquests registres per detectar i corregir problemes.\n\nÉs possible que alguns registres continguin informació sensible; per això només has de donar-hi accés a les aplicacions de confiança. \n\nEncara que no permetis que aquesta aplicació accedeixi a tots els registres del dispositiu, podrà accedir als seus propis registres. És possible que el fabricant del dispositiu també tingui accés a alguns registres o a informació del teu dispositiu.\n\nObtén més informació a g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"No tornis a mostrar"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vol mostrar porcions de l\'aplicació <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Edita"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index cbff6262de00..5938cc80f2de 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1960,7 +1960,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Tato položka na vašem zařízení <xliff:g id="DEVICE">%1$s</xliff:g> není k dispozici. Zkuste to na zařízení Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Tato položka na vašem zařízení <xliff:g id="DEVICE">%1$s</xliff:g> není k dispozici. Zkuste to na tabletu."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Tato položka na vašem zařízení <xliff:g id="DEVICE">%1$s</xliff:g> není k dispozici. Zkuste to na telefonu."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Tato aplikace byla vytvořena pro starší verzi systému Android a nemusí fungovat správně. Zkuste vyhledat aktualizace, případně kontaktujte vývojáře."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Zkontrolovat aktualizace"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Máte nové zprávy"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Zobrazíte je v aplikaci pro SMS"</string>
@@ -2053,8 +2054,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Povolit jednorázový přístup"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nepovolovat"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Do protokolů zařízení se zaznamenává, co se na zařízení děje. Aplikace tyto protokoly mohou používat k vyhledání a odstranění problémů.\n\nNěkteré protokoly mohou zahrnovat citlivé údaje. Přístup k protokolům zařízení proto povolte pouze aplikacím, kterým důvěřujete. \n\nPokud této aplikaci nepovolíte přístup ke všem protokolům zařízení, bude mít stále přístup ke svým vlastním protokolům. Výrobce zařízení může mít stále přístup k některým protokolům nebo informacím na vašem zařízení."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Do protokolů zařízení se zaznamenává, co se na zařízení děje. Aplikace tyto protokoly mohou používat k vyhledání a odstranění problémů.\n\nNěkteré protokoly mohou zahrnovat citlivé údaje. Přístup k protokolům zařízení proto povolte pouze aplikacím, kterým důvěřujete. \n\nPokud této aplikaci nepovolíte přístup ke všem protokolům zařízení, bude mít stále přístup ke svým vlastním protokolům. Výrobce zařízení může mít stále přístup k některým protokolům nebo informacím na vašem zařízení.\n\nDalší informace najdete na stránce g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Příště nezobrazovat"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Aplikace <xliff:g id="APP_0">%1$s</xliff:g> chce zobrazovat ukázky z aplikace <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Upravit"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 3b21d5b61da4..fa79a0952e23 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Du kan ikke gøre dette på din <xliff:g id="DEVICE">%1$s</xliff:g>. Prøv på din Android TV-enhed i stedet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Du kan ikke gøre dette på din <xliff:g id="DEVICE">%1$s</xliff:g>. Prøv på din tablet i stedet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Du kan ikke gøre dette på din <xliff:g id="DEVICE">%1$s</xliff:g>. Prøv på din telefon i stedet."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Denne app er lavet til en ældre version af Android og fungerer muligvis ikke korrekt. Prøv at søge efter opdateringer, eller kontakt udvikleren."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Søg efter opdatering"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Du har nye beskeder"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Åbn sms-appen for at se beskeden"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 16f312dd3bd4..eb59ebf2e748 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Auf deinem <xliff:g id="DEVICE">%1$s</xliff:g> ist kein Zugriff möglich. Versuch es stattdessen auf deinem Android TV-Gerät."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Auf deinem <xliff:g id="DEVICE">%1$s</xliff:g> ist kein Zugriff möglich. Versuch es stattdessen auf deinem Tablet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Auf deinem <xliff:g id="DEVICE">%1$s</xliff:g> ist kein Zugriff möglich. Versuch es stattdessen auf deinem Smartphone."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Diese App wurde für eine ältere Android-Version entwickelt und funktioniert möglicherweise nicht mehr richtig. Prüfe, ob Updates verfügbar sind oder kontaktiere den Entwickler."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Auf Updates prüfen"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Du hast neue Nachrichten"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Zum Ansehen SMS-App öffnen"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Einmaligen Zugriff zulassen"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nicht zulassen"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"In Geräteprotokollen wird aufgezeichnet, welche Aktionen auf deinem Gerät ausgeführt werden. Apps können diese Protokolle verwenden, um Probleme zu finden und zu beheben.\n\nEinige Protokolle enthalten unter Umständen vertrauliche Informationen, daher solltest du nur vertrauenswürdigen Apps den Zugriff auf alle Geräteprotokolle erlauben. \n\nWenn du dieser App keinen Zugriff auf alle Geräteprotokolle gewährst, kann sie trotzdem auf ihre eigenen Protokolle zugreifen. Dein Gerätehersteller hat möglicherweise auch Zugriff auf einige Protokolle oder Informationen auf deinem Gerät."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"In Geräteprotokollen wird aufgezeichnet, welche Aktionen auf deinem Gerät ausgeführt werden. Apps können diese Protokolle verwenden, um Probleme zu finden und zu beheben.\n\nEinige Protokolle enthalten unter Umständen vertrauliche Informationen. Daher solltest du nur vertrauenswürdigen Apps den Zugriff auf alle Geräteprotokolle erlauben. \n\nWenn du dieser App keinen Zugriff auf alle Geräteprotokolle gewährst, kann sie trotzdem auf ihre eigenen Protokolle zugreifen. Dein Gerätehersteller hat möglicherweise auch Zugang zu einigen Protokollen oder Informationen auf deinem Gerät.\n\nWeitere Informationen findest du unter g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Nicht mehr anzeigen"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> möchte Teile von <xliff:g id="APP_2">%2$s</xliff:g> anzeigen"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Bearbeiten"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 095af40643f5..a9dd1cf2b8fb 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Δεν είναι δυνατή η πρόσβαση σε αυτό το στοιχείο από τη συσκευή <xliff:g id="DEVICE">%1$s</xliff:g>. Δοκιμάστε στη συσκευή Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Δεν είναι δυνατή η πρόσβαση σε αυτό το στοιχείο από τη συσκευή <xliff:g id="DEVICE">%1$s</xliff:g>. Δοκιμάστε στο tablet σας."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Δεν είναι δυνατή η πρόσβαση σε αυτό το στοιχείο από τη συσκευή <xliff:g id="DEVICE">%1$s</xliff:g>. Δοκιμάστε στο τηλέφωνό σας."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Αυτή η εφαρμογή δημιουργήθηκε για παλαιότερη έκδοση του Android και μπορεί να μην λειτουργεί σωστά. Δοκιμάστε να ελέγξετε εάν υπάρχουν ενημερώσεις ή επικοινωνήστε με τον προγραμματιστή."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Αυτή η εφαρμογή σχεδιάστηκε για μια παλαιότερη έκδοση του Android. Ενδέχεται να μην λειτουργεί σωστά και δεν περιλαμβάνει τις πιο πρόσφατες λειτουργίες ασφάλειας και προστασίας απορρήτου. Ελέγξτε αν υπάρχει διαθέσιμη μια ενημέρωση ή επικοινωνήστε με τον προγραμματιστή της εφαρμογής."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Έλεγχος για ενημέρωση"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Έχετε νέα μηνύματα"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Άνοιγμα της εφαρμογής SMS για προβολή"</string>
@@ -2290,7 +2290,7 @@
<string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"Η εφαρμογή <xliff:g id="APP">%1$s</xliff:g> εκτελείται στο παρασκήνιο. Πατήστε για διαχείριση της χρήσης της μπαταρίας."</string>
<string name="notification_content_long_running_fgs" msgid="8258193410039977101">"Η εφαρμογή <xliff:g id="APP">%1$s</xliff:g> μπορεί να επηρεάσει τη διάρκεια ζωής μπαταρίας. Πατήστε για έλεγχο των ενεργών εφαρμογών."</string>
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Έλεγχος ενεργών εφαρμογών"</string>
- <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Δεν είναι δυνατή η πρόσβαση στην κάμερα του τηλεφώνου από τη συσκευή <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Δεν είναι δυνατή η πρόσβαση στην κάμερα τηλεφώνου από το <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Δεν είναι δυνατή η πρόσβαση στην κάμερα του tablet από τη συσκευή <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_secure_window" msgid="161700398158812314">"Δεν είναι δυνατή η πρόσβαση σε αυτό το στοιχείο κατά τη ροή. Δοκιμάστε στο τηλέφωνό σας."</string>
<string name="system_locale_title" msgid="711882686834677268">"Προεπιλογή συστήματος"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 5349d07bf829..1cc8d5014891 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your Android TV device instead."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your tablet instead."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your phone instead."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"This app was built for an older version of Android and may not work properly. Try checking for updates or contact the developer."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"This app was built for an older version of Android. It might not work properly and doesn\'t include the latest security and privacy protections. Check for an update or contact the app\'s developer."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Open SMS app to view"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 80156813adb8..6fa02f3b5a6c 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your Android TV device instead."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your tablet instead."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your phone instead."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"This app was built for an older version of Android and may not work properly. Try checking for updates or contact the developer."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"This app was built for an older version of Android. It might not work properly and doesn\'t include the latest security and privacy protections. Check for an update or contact the app\'s developer."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Open SMS app to view"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 7e5ebe4671b3..fac706e3f27f 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your Android TV device instead."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your tablet instead."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your phone instead."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"This app was built for an older version of Android and may not work properly. Try checking for updates or contact the developer."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"This app was built for an older version of Android. It might not work properly and doesn\'t include the latest security and privacy protections. Check for an update or contact the app\'s developer."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Open SMS app to view"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 904f07f55bfa..55c121ac4a01 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your Android TV device instead."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your tablet instead."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your phone instead."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"This app was built for an older version of Android and may not work properly. Try checking for updates or contact the developer."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"This app was built for an older version of Android. It might not work properly and doesn\'t include the latest security and privacy protections. Check for an update or contact the app\'s developer."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Open SMS app to view"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index b5051fd4ce3b..1b190e3e9b40 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎‎‎‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‎‎‏‎‎‏‎This can’t be accessed on your ‎‏‎‎‏‏‎<xliff:g id="DEVICE">%1$s</xliff:g>‎‏‎‎‏‏‏‎. Try on your Android TV device instead.‎‏‎‎‏‎"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‎This can’t be accessed on your ‎‏‎‎‏‏‎<xliff:g id="DEVICE">%1$s</xliff:g>‎‏‎‎‏‏‏‎. Try on your tablet instead.‎‏‎‎‏‎"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎This can’t be accessed on your ‎‏‎‎‏‏‎<xliff:g id="DEVICE">%1$s</xliff:g>‎‏‎‎‏‏‏‎. Try on your phone instead.‎‏‎‎‏‎"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.‎‏‎‎‏‎"</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‎This app was built for an older version of Android. It might not work properly and doesn\'t include the latest security and privacy protections. Check for an update, or contact the app\'s developer.‎‏‎‎‏‎"</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‏‎Check for update‎‏‎‎‏‎"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‎‏‏‎You have new messages‎‏‎‎‏‎"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‎‏‎‏‎‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‏‏‎‎‏‏‏‏‏‎Open SMS app to view‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 9d78de33864c..106935c24603 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1959,7 +1959,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"No se puede acceder a esto en tu <xliff:g id="DEVICE">%1$s</xliff:g>. Inténtalo en tu dispositivo Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"No se puede acceder a esto en tu <xliff:g id="DEVICE">%1$s</xliff:g>. Inténtalo en tu tablet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"No se puede acceder a esto en tu <xliff:g id="DEVICE">%1$s</xliff:g>. Inténtalo en tu teléfono."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Esta app se creó para una versión anterior de Android y es posible que no funcione correctamente. Busca actualizaciones o comunícate con el programador."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Esta app se compiló para una versión anterior de Android. Es posible que no funcione correctamente ni incluya las protecciones de la privacidad más recientes. Consulta si hay actualizaciones o comunícate con el desarrollador."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Buscar actualización"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Tienes mensajes nuevos"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Abrir app de SMS para ver el mensaje"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index f49de83d32d9..23a7c93b3012 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1956,10 +1956,11 @@
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Esta aplicación solicita seguridad adicional. Prueba en tu dispositivo Android TV."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Esta aplicación solicita seguridad adicional. Prueba en tu tablet."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Esta aplicación solicita seguridad adicional. Prueba en tu teléfono."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"No se puede acceder a este contenido en tu <xliff:g id="DEVICE">%1$s</xliff:g>. Prueba en tu dispositivo Android TV."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"No se puede acceder desde tu <xliff:g id="DEVICE">%1$s</xliff:g>. Prueba en tu dispositivo Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"No se puede acceder a este contenido en tu <xliff:g id="DEVICE">%1$s</xliff:g>. Prueba en tu tablet."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"No se puede acceder a este contenido en tu <xliff:g id="DEVICE">%1$s</xliff:g>. Prueba en tu teléfono."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Esta aplicación se ha diseñado para una versión anterior de Android y es posible que no funcione correctamente. Busca actualizaciones o ponte en contacto con el desarrollador."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"No se puede acceder desde tu <xliff:g id="DEVICE">%1$s</xliff:g>. Prueba en tu teléfono."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Buscar actualizaciones"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Tienes mensajes nuevos"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Abre la aplicación de SMS para ver el mensaje"</string>
@@ -2052,8 +2053,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir el acceso una vez"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"No permitir"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Los registros del dispositivo documentan lo que sucede en tu dispositivo. Las aplicaciones pueden usar estos registros para encontrar y solucionar problemas.\n\nComo algunos registros pueden contener información sensible, es mejor que solo permitas que accedan a ellos las aplicaciones en las que confíes. \n\nAunque no permitas que esta aplicación acceda a todos los registros del dispositivo, aún podrá acceder a sus propios registros. El fabricante de tu dispositivo aún puede acceder a algunos registros o información de tu dispositivo."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Los registros del dispositivo documentan lo que sucede en tu dispositivo. Las aplicaciones pueden usar estos registros para encontrar y solucionar problemas.\n\nComo algunos registros pueden contener información sensible, es mejor que solo permitas que accedan a ellos las aplicaciones en las que confíes. \n\nAunque no permitas que esta aplicación acceda a todos los registros del dispositivo, podrá seguir accediendo a sus propios registros. El fabricante de tu dispositivo aún puede acceder a algunos registros o información de tu dispositivo.\n\nObtén más información en g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"No volver a mostrar"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> quiere mostrar fragmentos de <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 70ad94ec6a69..961e67a34f62 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Sellele ei pääse teie seadmes <xliff:g id="DEVICE">%1$s</xliff:g> juurde. Proovige juurde pääseda oma Android TV seadmes."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Sellele ei pääse teie seadmes <xliff:g id="DEVICE">%1$s</xliff:g> juurde. Proovige juurde pääseda oma tahvelarvutis."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Sellele ei pääse teie seadmes <xliff:g id="DEVICE">%1$s</xliff:g> juurde. Proovige juurde pääseda oma telefonis."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"See rakendus on loodud Androidi vanema versiooni jaoks ega pruugi õigesti töötada. Otsige värskendusi või võtke ühendust arendajaga."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Otsi värskendust"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Teile on uusi sõnumeid"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Avage vaatamiseks SMS-rakendus"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Luba ühekordne juurdepääs"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ära luba"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Seadmelogid jäädvustavad, mis teie seadmes toimub. Rakendused saavad neid logisid kasutada probleemide tuvastamiseks ja lahendamiseks.\n\nMõned logid võivad sisaldada tundlikku teavet, seega lubage juurdepääs kõigile seadmelogidele ainult rakendustele, mida usaldate. \n\nKui te ei luba sellel rakendusel kõigile seadmelogidele juurde pääseda, pääseb see siiski juurde oma logidele. Teie seadme tootja võib teie seadmes siiski teatud logidele või teabele juurde pääseda."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Seadmelogid jäädvustavad, mis teie seadmes toimub. Rakendused saavad neid logisid kasutada probleemide tuvastamiseks ja lahendamiseks.\n\nMõned logid võivad sisaldada tundlikku teavet, seega lubage juurdepääs kõigile seadmelogidele ainult rakendustele, mida usaldate. \n\nKui te ei luba sellel rakendusel kõigile seadmelogidele juurde pääseda, pääseb see siiski juurde oma logidele. Teie seadme tootja võib teie seadmes siiski teatud logidele või teabele juurde pääseda.\n\nLugege lisateavet aadressil g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ära kuva uuesti"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Rakendus <xliff:g id="APP_0">%1$s</xliff:g> soovib näidata rakenduse <xliff:g id="APP_2">%2$s</xliff:g> lõike"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Muuda"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 8b447d1ea501..e5c384f32d3e 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -519,9 +519,9 @@
<string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Wi-Fi sarearen bidez gailu guztiei bidalitako paketeak jasotzeko baimena ematen die aplikazioei multidifusio-helbideak erabilita, ez tableta soilik. Multidifusiokoa ez den moduak baino bateria gehiago erabiltzen du."</string>
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Multidifusio-helbideak erabiliz wifi-sare bateko gailu guztiei (ez bakarrik Android TV gailuari) bidalitako paketeak jasotzeko baimena ematen die aplikazioei. Multidifusiokoa ez den moduak baino bateria gehiago erabiltzen du."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Wi-Fi sarearen bidez gailu guztiei bidalitako paketeak jasotzeko baimena ematen die aplikazioei multidifusio-helbideak erabilita, ez telefonoa soilik. Multidifusiokoa ez den moduak baino bateria gehiago erabiltzen du."</string>
- <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"atzitu Bluetooth-aren ezarpenak"</string>
+ <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"atzitu Bluetootharen ezarpenak"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Tokiko Bluetooth tableta konfiguratzea eta urruneko gailuak detektatzea eta haiekin parekatzea baimentzen die aplikazioei."</string>
- <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Android TV gailuan Bluetooth-a konfiguratzeko eta urruneko gailuak hautemateko eta haiekin parekatzeko baimena ematen die aplikazioei."</string>
+ <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Android TV gailuan Bluetootha konfiguratzeko eta urruneko gailuak hautemateko eta haiekin parekatzeko baimena ematen die aplikazioei."</string>
<string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Tokiko Bluetooth telefonoa konfiguratzea eta urruneko gailuak detektatzea eta haiekin parekatzea baimentzen die aplikazioei."</string>
<string name="permlab_accessWimaxState" msgid="7029563339012437434">"WiMAX sarera konektatzea eta deskonektatzea"</string>
<string name="permdesc_accessWimaxState" msgid="5372734776802067708">"WiMAX gaituta dagoen zehazteko eta konektatutako WiMAX sareei buruzko informazioa ikusteko baimena ematen die aplikazioei."</string>
@@ -1308,7 +1308,7 @@
<string-array name="network_switch_type_name">
<item msgid="2255670471736226365">"datu-konexioa"</item>
<item msgid="5520925862115353992">"Wifia"</item>
- <item msgid="1055487873974272842">"Bluetooth-a"</item>
+ <item msgid="1055487873974272842">"Bluetootha"</item>
<item msgid="1616528372438698248">"Ethernet"</item>
<item msgid="9177085807664964627">"VPNa"</item>
</string-array>
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Aplikazioa ezin da <xliff:g id="DEVICE">%1$s</xliff:g> erabilita atzitu. Gailu horren ordez, erabili Android TV darabilen bat."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Aplikazioa ezin da <xliff:g id="DEVICE">%1$s</xliff:g> erabilita atzitu. Gailu horren ordez, erabili tableta."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Aplikazioa ezin da <xliff:g id="DEVICE">%1$s</xliff:g> erabilita atzitu. Gailu horren ordez, erabili telefonoa."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Aplikazioa Android-en bertsio zaharrago baterako sortu zenez, baliteke behar bezala ez funtzionatzea. Bilatu eguneratzerik baden, edo jarri garatzailearekin harremanetan."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Bilatu eguneratzeak"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Mezu berriak dituzu"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Mezuak ikusteko, ireki SMSetarako aplikazioa"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Eman behin erabiltzeko baimena"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ez eman baimenik"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Gailuko erregistroetan gailuan gertatzen den guztia gordetzen da. Arazoak bilatu eta konpontzeko erabil ditzakete aplikazioek erregistro horiek.\n\nBaliteke erregistro batzuek kontuzko informazioa edukitzea. Beraz, eman gailuko erregistro guztiak atzitzeko baimena fidagarritzat jotzen dituzun aplikazioei bakarrik. \n\nNahiz eta gailuko erregistro guztiak atzitzeko baimena ez eman aplikazio honi, aplikazioak hari dagozkion erregistroak atzitu ahalko ditu. Gainera, baliteke gailuaren fabrikatzaileak gailuko erregistro edo datu batzuk atzitu ahal izatea."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Gailuko erregistroetan gailuan gertatzen den guztia gordetzen da. Arazoak bilatu eta konpontzeko erabil ditzakete aplikazioek erregistro horiek.\n\nBaliteke erregistro batzuek kontuzko informazioa edukitzea. Beraz, eman gailuko erregistro guztiak atzitzeko baimena fidagarritzat jotzen dituzun aplikazioei bakarrik. \n\nNahiz eta gailuko erregistro guztiak atzitzeko baimena ez eman aplikazio honi, aplikazioak hari dagozkion erregistroak atzitu ahalko ditu. Gainera, baliteke gailuaren fabrikatzaileak gailuko erregistro edo datu batzuk atzitu ahal izatea.\n\nLortu informazio gehiago g.co/android/devicelogs helbidean."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ez erakutsi berriro"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> aplikazioak <xliff:g id="APP_2">%2$s</xliff:g> aplikazioaren zatiak erakutsi nahi ditu"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Editatu"</string>
@@ -2110,7 +2110,7 @@
<string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g> kalkulu-orria"</string>
<string name="mime_type_presentation" msgid="1145384236788242075">"Aurkezpena"</string>
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"<xliff:g id="EXTENSION">%1$s</xliff:g> aurkezpena"</string>
- <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"Bluetooth-ak aktibatuta jarraituko du hegaldi moduan"</string>
+ <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"Bluetoothak aktibatuta jarraituko du hegaldi moduan"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"Kargatzen"</string>
<string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} eta beste # fitxategi}other{{file_name} eta beste # fitxategi}}"</string>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Ez dago edukia partekatzeko pertsona gomendaturik"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 9007d8ee990b..58a7f6293f67 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"‏نمی‌توان در <xliff:g id="DEVICE">%1$s</xliff:g> به این مورد دسترسی داشت. دسترسی به آن را در دستگاه Android TV امتحان کنید."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"نمی‌توان در <xliff:g id="DEVICE">%1$s</xliff:g> به این مورد دسترسی داشت. دسترسی به آن را در رایانه لوحی‌تان امتحان کنید."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"نمی‌توان در <xliff:g id="DEVICE">%1$s</xliff:g> به این مورد دسترسی داشت. دسترسی به آن را در تلفنتان امتحان کنید."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏این برنامه برای نسخه قدیمی‌تری از Android ساخته شده است و ممکن است درست کار نکند. وجود به‌روزرسانی را بررسی کنید یا با برنامه‌نویس تماس بگیرید."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"‏این برنامه برای نسخه قدیمی‌تری از Android ساخته شده است. احتمال دارد به‌درستی کار نکند و شامل جدیدترین محافظت‌های حریم خصوصی و امنیت نمی‌شود. بررسی کنید به‌روزرسانی دردسترس باشد یا با توسعه‌دهنده برنامه تماس بگیرید."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"بررسی وجود به‌روزرسانی"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"پیام‌های جدیدی دارید"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"برای مشاهده، برنامه پیامک را باز کنید"</string>
@@ -2051,8 +2051,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"مجاز کردن دسترسی یک‌باره"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"اجازه ندادن"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"گزارش‌های دستگاه آنچه را در دستگاهتان رخ می‌دهد ثبت می‌کند. برنامه‌ها می‌توانند از این گزارش‌ها برای پیدا کردن مشکلات و رفع آن‌ها استفاده کنند.\n\nبرخی‌از گزارش‌ها ممکن است حاوی اطلاعات حساس باشند، بنابراین فقط به برنامه‌های مورداعتمادتان اجازه دسترسی به همه گزارش‌های دستگاه را بدهید. \n\nاگر به این برنامه اجازه ندهید به همه گزارش‌های دستگاه دسترسی داشته باشد، همچنان می‌تواند به گزارش‌های خودش دسترسی داشته باشد. سازنده دستگاه نیز ممکن است همچنان بتواند به برخی‌از گزارش‌ها یا اطلاعات دستگاهتان دسترسی داشته باشد."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"‏گزارش‌های دستگاه آنچه را در دستگاهتان رخ می‌دهد ثبت می‌کند. برنامه‌ها می‌توانند از این گزارش‌ها برای پیدا کردن مشکلات و رفع آن‌ها استفاده کنند.\n\nبرخی‌از گزارش‌ها ممکن است حاوی اطلاعات حساس باشند، بنابراین فقط به برنامه‌های مورداعتمادتان اجازه دسترسی به همه گزارش‌های دستگاه را بدهید. \n\nاگر به این برنامه اجازه ندهید به همه گزارش‌های دستگاه دسترسی داشته باشد، همچنان می‌تواند به گزارش‌های خودش دسترسی داشته باشد. سازنده دستگاه نیز ممکن است همچنان بتواند به برخی‌از گزارش‌ها یا اطلاعات دستگاهتان دسترسی داشته باشد.\n\nاطلاعات بیشتر: g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"دوباره نشان داده نشود"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> می‌خواهد تکه‌های <xliff:g id="APP_2">%2$s</xliff:g> را نشان دهد"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"ویرایش"</string>
@@ -2291,7 +2290,7 @@
<string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"<xliff:g id="APP">%1$s</xliff:g> در پس‌زمینه درحال اجرا است. برای مدیریت مصرف باتری ضربه بزنید."</string>
<string name="notification_content_long_running_fgs" msgid="8258193410039977101">"<xliff:g id="APP">%1$s</xliff:g> ممکن است بر عمر باتری تأثیر بگذارد. برای مرور برنامه‌های فعال، ضربه بزنید."</string>
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"بررسی برنامه‌های فعال"</string>
- <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"نمی‌توان از <xliff:g id="DEVICE">%1$s</xliff:g> شما به دوربین تلفن دسترسی داشت"</string>
+ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"از <xliff:g id="DEVICE">%1$s</xliff:g> به دوربین تلفن دسترسی ندارید"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"نمی‌توان از <xliff:g id="DEVICE">%1$s</xliff:g> شما به دوربین رایانه لوحی دسترسی داشت"</string>
<string name="vdm_secure_window" msgid="161700398158812314">"درحین جاری‌سازی، نمی‌توانید به آن دسترسی داشته باشید. دسترسی به آن را در تلفنتان امتحان کنید."</string>
<string name="system_locale_title" msgid="711882686834677268">"پیش‌فرض سیستم"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 5afb3091e743..22584c3f6a84 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"<xliff:g id="DEVICE">%1$s</xliff:g> ei saa pääsyä sovellukseen. Kokeile striimausta Android TV ‑laitteella."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"<xliff:g id="DEVICE">%1$s</xliff:g> ei saa pääsyä sovellukseen. Kokeile striimausta tabletilla."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"<xliff:g id="DEVICE">%1$s</xliff:g> ei saa pääsyä sovellukseen. Kokeile striimausta puhelimella."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Tämä sovellus on suunniteltu vanhemmalle Android-versiolle eikä välttämättä toimi oikein. Kokeile tarkistaa päivitykset tai ottaa yhteyttä kehittäjään."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Tarkista päivitykset"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Sinulle on uusia viestejä"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Katso avaamalla tekstiviestisovellus."</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Salli kertaluonteinen pääsy"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Älä salli"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Laitteen tapahtumat tallentuvat laitelokeihin. Niiden avulla sovellukset voivat löytää ja korjata ongelmia.\n\nJotkin lokit voivat sisältää arkaluontoista tietoa, joten salli pääsy kaikkiin laitelokeihin vain sovelluksille, joihin luotat. \n\nJos et salli tälle sovellukselle pääsyä kaikkiin laitelokeihin, sillä on kuitenkin pääsy sen omiin lokeihin. Laitteen valmistajalla voi olla pääsy joihinkin lokeihin tai tietoihin laitteella."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Laitteen tapahtumat tallentuvat laitelokeihin. Niiden avulla sovellukset voivat löytää ja korjata ongelmia.\n\nJotkin lokit voivat sisältää arkaluontoista tietoa, joten salli pääsy kaikkiin laitelokeihin vain sovelluksille, joihin luotat. \n\nJos et salli tälle sovellukselle pääsyä kaikkiin laitelokeihin, sillä on kuitenkin pääsy sen omiin lokeihin. Laitteen valmistajalla voi olla pääsy joihinkin lokeihin tai tietoihin laitteella.\n\nLue lisää osoitteessa g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Älä näytä uudelleen"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> haluaa näyttää osia sovelluksesta <xliff:g id="APP_2">%2$s</xliff:g>."</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Muokkaa"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index fc4bd0a6fd64..5150da961437 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1401,7 +1401,7 @@
<string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"Analyse de l\'espace de stockage sur le support en cours…"</string>
<string name="ext_media_new_notification_title" msgid="3517407571407687677">"Nouveau périphérique <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> ne fonctionne pas"</string>
- <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Toucher pour configurer"</string>
+ <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Touchez pour configurer"</string>
<string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"Sélectionnez pour configurer"</string>
<string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"Vous devrez peut-être reformater l\'appareil. Touchez pour l\'éjecter."</string>
<string name="ext_media_ready_notification_message" msgid="7509496364380197369">"Pour stocker des photos, des vidéos, de la musique et plus encore"</string>
@@ -1413,7 +1413,7 @@
<string name="ext_media_unmountable_notification_message" product="automotive" msgid="2274596120715020680">"Vous devrez peut-être reformater l\'appareil. Touchez pour l\'éjecter."</string>
<string name="ext_media_unsupported_notification_title" msgid="3487534182861251401">"<xliff:g id="NAME">%s</xliff:g> détecté"</string>
<string name="ext_media_unsupported_notification_title" product="automotive" msgid="6004193172658722381">"<xliff:g id="NAME">%s</xliff:g> ne fonctionne pas"</string>
- <string name="ext_media_unsupported_notification_message" msgid="8463636521459807981">"Toucher pour configurer ."</string>
+ <string name="ext_media_unsupported_notification_message" msgid="8463636521459807981">"Touchez pour configurer ."</string>
<string name="ext_media_unsupported_notification_message" product="tv" msgid="1595482802187036532">"Sélectionner pour configurer <xliff:g id="NAME">%s</xliff:g> dans un format pris en charge."</string>
<string name="ext_media_unsupported_notification_message" product="automotive" msgid="3412494732736336330">"Vous devrez peut-être reformater l\'appareil"</string>
<string name="ext_media_badremoval_notification_title" msgid="4114625551266196872">"Retrait inattendu de la mémoire « <xliff:g id="NAME">%s</xliff:g> »"</string>
@@ -1950,16 +1950,16 @@
<string name="app_streaming_blocked_title_for_settings_dialog" product="tv" msgid="196994247017450357">"Paramètres Android TV non accessibles"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="tablet" msgid="8222710146267948647">"Paramètres de la tablette non accessibles"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"Paramètres du téléphone non accessibles"</string>
- <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Impossible d\'accéder à ce contenu sur votre appareil <xliff:g id="DEVICE">%1$s</xliff:g> pour le moment. Essayez sur votre appareil Android TV à la place."</string>
- <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Impossible d\'accéder à ce contenu sur votre appareil <xliff:g id="DEVICE">%1$s</xliff:g> pour le moment. Essayez sur votre tablette à la place."</string>
- <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Impossible d\'accéder à ce contenu sur votre appareil <xliff:g id="DEVICE">%1$s</xliff:g> pour le moment. Essayez sur votre téléphone à la place."</string>
+ <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Impossible d\'accéder à ce contenu sur votre <xliff:g id="DEVICE">%1$s</xliff:g> pour le moment. Essayez sur votre appareil Android TV à la place."</string>
+ <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Impossible d\'accéder à ce contenu sur votre <xliff:g id="DEVICE">%1$s</xliff:g> pour le moment. Essayez sur votre tablette à la place."</string>
+ <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Impossible d\'accéder à ce contenu sur votre <xliff:g id="DEVICE">%1$s</xliff:g> pour le moment. Essayez sur votre téléphone à la place."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Cette application demande une sécurité supplémentaire. Essayez sur votre appareil Android TV à la place."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Cette application demande une sécurité supplémentaire. Essayez sur votre tablette à la place."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Cette application demande une sécurité supplémentaire. Essayez sur votre téléphone à la place."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Impossible d\'accéder à ce contenu sur votre appareil <xliff:g id="DEVICE">%1$s</xliff:g>. Essayez sur votre appareil Android TV à la place."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Impossible d\'accéder à ce contenu sur votre appareil <xliff:g id="DEVICE">%1$s</xliff:g>. Essayez sur votre tablette à la place."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Impossible d\'accéder à ce contenu sur votre appareil <xliff:g id="DEVICE">%1$s</xliff:g>. Essayez sur votre téléphone à la place."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Cette application a été conçue pour une ancienne version d\'Android et pourrait ne pas fonctionner correctement. Essayez de vérifier les mises à jour ou communiquez avec son développeur."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Impossible d\'accéder à ce contenu sur votre <xliff:g id="DEVICE">%1$s</xliff:g>. Essayez sur votre appareil Android TV à la place."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Impossible d\'accéder à ce contenu sur votre <xliff:g id="DEVICE">%1$s</xliff:g>. Essayez sur votre tablette à la place."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Impossible d\'accéder à ce contenu sur votre <xliff:g id="DEVICE">%1$s</xliff:g>. Essayez sur votre téléphone à la place."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Cette application a été conçue pour une ancienne version d\'Android. Elle pourrait ne pas fonctionner correctement, et ne comprend pas les dernières protections des données confidentielles et de sécurité. Vérifiez s\'il existe une mise à jour ou contactez le développeur de l\'application."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Vérifier la présence de mises à jour"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Vous avez de nouveaux messages"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Ouvrez l\'application de messagerie texte pour l\'afficher"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index a30530d8cb13..374d83854d12 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -577,7 +577,7 @@
<string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Utilisez la biométrie ou le verrouillage de l\'écran pour continuer"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Matériel biométrique indisponible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentification annulée"</string>
- <string name="biometric_not_recognized" msgid="5106687642694635888">"Non reconnu"</string>
+ <string name="biometric_not_recognized" msgid="5106687642694635888">"Non reconnue"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentification annulée"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Aucun code, schéma ni mot de passe n\'est défini"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Erreur d\'authentification"</string>
@@ -1959,7 +1959,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Impossible d\'accéder à ces paramètres sur votre <xliff:g id="DEVICE">%1$s</xliff:g>. Essayez plutôt sur votre appareil Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Impossible d\'accéder à ces paramètres sur votre <xliff:g id="DEVICE">%1$s</xliff:g>. Essayez plutôt sur votre tablette."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Impossible d\'accéder à ces paramètres sur votre <xliff:g id="DEVICE">%1$s</xliff:g>. Essayez plutôt sur votre téléphone."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Cette application a été conçue pour une ancienne version d\'Android et risque de ne pas fonctionner correctement. Recherchez des mises à jour ou contactez le développeur."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Rechercher une mise à jour"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Vous avez de nouveaux messages"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Ouvrir l\'application de SMS pour afficher le message"</string>
@@ -2052,8 +2053,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Autoriser un accès unique"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ne pas autoriser"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Les journaux enregistrent ce qui se passe sur votre appareil. Les applis peuvent les utiliser pour rechercher et résoudre les problèmes.\n\nCertains journaux pouvant contenir des infos sensibles, autorisez uniquement les applis de confiance à accéder à tous les journaux de l\'appareil. \n\nSi vous refusez à cette appli l\'accès à tous les journaux de l\'appareil, elle a quand même accès aux siens. Le fabricant de l\'appareil peut accéder à certains journaux ou certaines infos sur votre appareil."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Les journaux enregistrent ce qui se passe sur votre appareil. Les applis peuvent les utiliser pour rechercher et résoudre des problèmes.\n\nCertains journaux pouvant contenir des infos sensibles, autorisez uniquement les applis de confiance à accéder à tous les journaux de l\'appareil. \n\nSi vous refusez à cette appli l\'accès à tous les journaux de l\'appareil, elle a quand même accès aux siens. Le fabricant de l\'appareil peut accéder à certains journaux ou certaines infos sur votre appareil.\n\nEn savoir plus sur g.co/android/devicelogs"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne plus afficher"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> souhaite afficher des éléments de <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Modifier"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 33c5cec5d8ce..c852dc1f007c 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Non se puido acceder a este contido desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>). Proba a facelo desde o dispositivo con Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Non se puido acceder a este contido desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>). Proba a facelo desde a tableta."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Non se puido acceder a este contido desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>). Proba a facelo desde o teléfono."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Esta aplicación deseñouse para unha versión anterior de Android e quizais non funcione correctamente. Proba a buscar actualizacións ou contacta co programador."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Buscar actualización"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Tes mensaxes novas"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Abre a aplicación de SMS para ver as mensaxes"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir acceso unha soa vez"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Non permitir"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Os rexistros do dispositivo dan conta do que ocorre neste. As aplicacións poden usalos para buscar problemas e solucionalos.\n\nAlgúns poden conter información confidencial, polo que che recomendamos que só permitas que accedan a todos os rexistros do dispositivo as aplicacións nas que confíes. \n\nEsta aplicación pode acceder aos seus propios rexistros aínda que non lle permitas acceder a todos. É posible que o fabricante do dispositivo teña acceso a algúns rexistros ou á información do teu dispositivo."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Os rexistros do dispositivo dan conta do que ocorre neste. As aplicacións poden usalos para buscar problemas e solucionalos.\n\nAlgúns poden conter información confidencial, polo que che recomendamos que só permitas que accedan a todos os rexistros do dispositivo as aplicacións nas que confíes. \n\nEsta aplicación pode acceder aos seus propios rexistros aínda que non lle permitas acceder a todos. É posible que o fabricante do dispositivo teña acceso a algúns rexistros ou á información do teu dispositivo.\n\nConsulta máis información en g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Non amosar outra vez"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> quere mostrar fragmentos de aplicación de <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index c22f1557dc4d..42bad0a5107c 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"આને તમારા <xliff:g id="DEVICE">%1$s</xliff:g> પર ઍક્સેસ કરી શકાતી નથી. તેના બદલે તમારા Android TV ડિવાઇસ પર પ્રયાસ કરો."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"આને તમારા <xliff:g id="DEVICE">%1$s</xliff:g> પર ઍક્સેસ કરી શકાતી નથી. તેના બદલે તમારા ટૅબ્લેટ પર પ્રયાસ કરો."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"આને તમારા <xliff:g id="DEVICE">%1$s</xliff:g> પર ઍક્સેસ કરી શકાતી નથી. તેના બદલે તમારા ફોન પર પ્રયાસ કરો."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"આ ઍપ Androidના જૂના વર્ઝન માટે બનાવવામાં આવ્યું હતું અને તે કદાચ તે યોગ્ય રીતે કાર્ય કરી શકશે નહીં. અપડેટ માટે તપાસવાનો પ્રયાસ કરો અથવા ડેવલપરનો સંપર્ક કરો."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Androidના કોઈ જૂના વર્ઝન માટે આ ઍપ બનાવવામાં આવી હતી. તે કદાચ યોગ્ય રીતે કામ કરતી નથી અને તેમાં નવીનતમ સુરક્ષા અને પ્રાઇવસી સંબંધિત સંરક્ષણો શામેલ નથી. કોઈ અપડેટ ચેક કરો અથવા ઍપના ડેવલપરનો સંપર્ક કરો."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"અપડેટ માટે તપાસો"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"તમારી પાસે નવા સંદેશા છે"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"જોવા માટે SMS ઍપ્લિકેશન ખોલો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index c47322f8b306..e4c7ab0c12a3 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1948,7 +1948,7 @@
<string name="app_streaming_blocked_title_for_playstore_dialog" msgid="8149823099822897538">"Play Store उपलब्ध नहीं है"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="tv" msgid="196994247017450357">"Android TV की सेटिंग उपलब्ध नहीं हैं"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="tablet" msgid="8222710146267948647">"टैबलेट की सेटिंग उपलब्ध नहीं हैं"</string>
- <string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"फ़ोन की सेटिंग उपलब्ध नहीं हैं"</string>
+ <string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"फ़ोन की सेटिंग उपलब्ध नहीं है"</string>
<string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"इस समय, आपके <xliff:g id="DEVICE">%1$s</xliff:g> पर इसे ऐक्सेस नहीं किया जा सकता. इसके बजाय, अपने Android TV डिवाइस पर कोशिश करें."</string>
<string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"इस समय, आपके <xliff:g id="DEVICE">%1$s</xliff:g> पर इसे ऐक्सेस नहीं किया जा सकता. इसके बजाय, अपने टैबलेट पर कोशिश करें."</string>
<string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"इस समय, आपके <xliff:g id="DEVICE">%1$s</xliff:g> पर इसे ऐक्सेस नहीं किया जा सकता. इसके बजाय, अपने फ़ोन पर कोशिश करें."</string>
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"आपके <xliff:g id="DEVICE">%1$s</xliff:g> पर इसे ऐक्सेस नहीं किया जा सकता. इसके बजाय, अपने Android TV डिवाइस पर ऐक्सेस करने की कोशिश करें."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"आपके <xliff:g id="DEVICE">%1$s</xliff:g> पर इसे ऐक्सेस नहीं किया जा सकता. इसके बजाय, अपने टैबलेट पर ऐक्सेस करने की कोशिश करें."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"आपके <xliff:g id="DEVICE">%1$s</xliff:g> पर इसे ऐक्सेस नहीं किया जा सकता. इसके बजाय, अपने फ़ोन पर ऐक्सेस करने की कोशिश करें."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"यह ऐप्लिकेशन Android के पुराने वर्शन के लिए बनाया गया था, इसलिए हो सकता है कि यह सही से काम न करे. देखें कि अपडेट मौजूद हैं या नहीं, या फिर डेवलपर से संपर्क करें."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"देखें कि अपडेट मौजूद है या नहीं"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"आपके पास नए संदेश हैं"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"देखने के लिए मैसेज (एसएमएस) ऐप खोलें"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"एक बार ऐक्सेस करने की अनुमति दें"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"अनुमति न दें"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"डिवाइस लॉग में, आपके डिवाइस पर की गई कार्रवाइयां रिकॉर्ड होती हैं. ऐप्लिकेशन, इन लॉग का इस्तेमाल गड़बड़ियां ढूंढने और उन्हें ठीक करने के लिए करते हैं.\n\nकुछ लॉग में संवेदनशील जानकारी हो सकती है. इसलिए, सिर्फ़ भरोसेमंद ऐप्लिकेशन को डिवाइस लॉग का ऐक्सेस दें. \n\nअगर इस ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस नहीं दिया जाता है, तब भी यह डिवाइस पर अपने लॉग को ऐक्सेस कर सकता है. डिवाइस को बनाने वाली कंपनी अब भी डिवाइस के कुछ लॉग या जानकारी को ऐक्सेस कर सकती है."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"डिवाइस लॉग में आपके डिवाइस पर की गई कार्रवाइयां रिकॉर्ड होती हैं. ऐप्लिकेशन, इन लॉग का इस्तेमाल गड़बड़ियां ढूंढने और उन्हें सही करने के लिए करता है.\n\nकुछ लॉग में संवेदनशील जानकारी हो सकती है. इसलिए, सिर्फ़ भरोसेमंद ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस दें. \n\nअगर किसी ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस नहीं दिया जाता है, तब भी वह डिवाइस पर अपने लॉग को ऐक्सेस कर सकता है. डिवाइस को बनाने वाली कंपनी अब भी डिवाइस के कुछ लॉग या जानकारी को ऐक्सेस कर सकती है.\n\nज़्यादा जानने के लिए, g.co/android/devicelogs पर जाएं."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"फिर से न दिखाएं"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>, <xliff:g id="APP_2">%2$s</xliff:g> के हिस्से (स्लाइस) दिखाना चाहता है"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"बदलाव करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index f4bcfc5f0459..706f19d3e36f 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1951,15 +1951,15 @@
<string name="app_streaming_blocked_title_for_settings_dialog" product="tablet" msgid="8222710146267948647">"Postavke tableta nisu dostupne"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"Postavke telefona nisu dostupne"</string>
<string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Trenutačno toj aplikaciji nije moguće pristupiti na vašem uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Pokušajte joj pristupiti na Android TV uređaju."</string>
- <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Trenutačno toj aplikaciji nije moguće pristupiti na vašem uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Pokušajte joj pristupiti na svojem tabletu."</string>
- <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Trenutačno toj aplikaciji nije moguće pristupiti na vašem uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Pokušajte joj pristupiti na svojem telefonu."</string>
+ <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Trenutačno toj aplikaciji nije moguće pristupiti na vašem uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Pokušajte joj pristupiti na tabletu."</string>
+ <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Trenutačno toj aplikaciji nije moguće pristupiti na vašem uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Pokušajte joj pristupiti na telefonu."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Ta aplikacija zahtijeva dodatnu sigurnost. Pokušajte joj pristupiti na Android TV uređaju."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Ta aplikacija zahtijeva dodatnu sigurnost. Pokušajte joj pristupiti na tabletu."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Ta aplikacija zahtijeva dodatnu sigurnost. Pokušajte joj pristupiti na telefonu."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Toj aplikaciji nije moguće pristupiti na vašem uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Pokušajte joj pristupiti na Android TV uređaju."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Toj aplikaciji nije moguće pristupiti na vašem uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Pokušajte joj pristupiti na svojem tabletu."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Toj aplikaciji nije moguće pristupiti na vašem uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Pokušajte joj pristupiti na svojem telefonu."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ova je aplikacija razvijena za stariju verziju Androida i možda neće funkcionirati pravilno. Potražite ažuriranja ili se obratite razvojnom programeru."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Toj aplikaciji nije moguće pristupiti na vašem uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Pokušajte joj pristupiti na tabletu."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Toj aplikaciji nije moguće pristupiti na vašem uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Pokušajte joj pristupiti na telefonu."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ova je aplikacija razvijena za stariju verziju Androida. Možda neće funkcionirati pravilno i ne uključuje najnovije zaštite sigurnosti i privatnosti. Provjerite je li za tu aplikaciju dostupno ažuriranje ili se obratite razvojnom programeru."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Provjeri ažuriranja"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nove poruke"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Otvorite SMS aplikaciju da biste pregledali poruke"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index b2dd4b1ef967..71687d4bc2a9 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Ehhez nem lehet hozzáférni a következő eszközön: <xliff:g id="DEVICE">%1$s</xliff:g>. Próbálja újra az Android TV-eszközön."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Ehhez nem lehet hozzáférni a következő eszközön: <xliff:g id="DEVICE">%1$s</xliff:g>. Próbálja újra a táblagépen."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ehhez nem lehet hozzáférni a következő eszközön: <xliff:g id="DEVICE">%1$s</xliff:g>. Próbálja újra a telefonon."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ez az alkalmazás az Android egyik korábbi verziójához készült, így elképzelhető, hogy nem működik majd megfelelően ezen a rendszeren. Keressen frissítéseket, vagy vegye fel a kapcsolatot a fejlesztővel."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ez az alkalmazás az Android egy korábbi verziójához készült. Előfordulhat, hogy nem működik megfelelően, és nem tartalmazza a legfrissebb biztonsági és adatvédelmi megoldásokat. Keressen frissítést, vagy forduljon az app fejlesztőjéhez."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Frissítés keresése"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Új üzenetei érkeztek"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"SMS-alkalmazás megnyitása a megtekintéshez"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 964abb1af199..540ab7f6595e 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Այս գործառույթը հասանելի չէ <xliff:g id="DEVICE">%1$s</xliff:g> սարքում։ Օգտագործեք ձեր Android TV սարքը։"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Այս գործառույթը հասանելի չէ <xliff:g id="DEVICE">%1$s</xliff:g> սարքում։ Օգտագործեք ձեր պլանշետը։"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Այս գործառույթը հասանելի չէ <xliff:g id="DEVICE">%1$s</xliff:g> սարքում։ Օգտագործեք ձեր հեռախոսը։"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Այս հավելվածը ստեղծվել է Android-ի ավելի հին տարբերակի համար և կարող է պատշաճ չաշխատել: Ստուգեք թարմացումների առկայությունը կամ դիմեք մշակողին:"</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Ստուգել նոր տարբերակի առկայությունը"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Դուք ունեք նոր հաղորդագրություններ"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Դիտելու համար բացել SMS-ների փոխանակման հավելվածը"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Թույլատրել մեկանգամյա մուտքը"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Չթույլատրել"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Այն, ինչ տեղի է ունենում ձեր սարքում, գրանցվում է սարքի մատյաններում։ Հավելվածները կարող են դրանք օգտագործել անսարքությունները հայտնաբերելու և վերացնելու նպատակով։\n\nՔանի որ որոշ մատյաններ անձնական տեղեկություններ են պարունակում, խորհուրդ ենք տալիս հասանելի դարձնել ձեր սարքի բոլոր մատյանները միայն այն հավելվածներին, որոնց վստահում եք։ \n\nԵթե այս հավելվածին նման թույլտվություն չեք տվել, դրան նախկինի պես հասանելի կլինեն իր մատյանները։ Հնարավոր է՝ ձեր սարքի արտադրողին ևս հասանելի լինեն սարքի որոշ մատյաններ և տեղեկություններ։"</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Այն, ինչ տեղի է ունենում ձեր սարքում, գրանցվում է սարքի մատյաններում։ Հավելվածները կարող են դրանք օգտագործել անսարքությունները հայտնաբերելու և վերացնելու նպատակով։\n\nՔանի որ որոշ մատյաններ անձնական տեղեկություններ են պարունակում, խորհուրդ ենք տալիս հասանելի դարձնել ձեր սարքի բոլոր մատյանները միայն այն հավելվածներին, որոնց վստահում եք։ \n\nԵթե այս հավելվածին նման թույլտվություն չեք տվել, դրան նախկինի պես հասանելի կլինեն իր մատյանները։ Հնարավոր է՝ ձեր սարքի արտադրողին ևս հասանելի լինեն սարքի որոշ մատյաններ և տեղեկություններ։\n\nՄանրամասների համար այցելեք g.co/android/devicelogs էջ։"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Այլևս ցույց չտալ"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> հավելվածն ուզում է ցուցադրել հատվածներ <xliff:g id="APP_2">%2$s</xliff:g> հավելվածից"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Փոփոխել"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index aba836ac613c..b2dd707bfcf1 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Setelan ini tidak dapat diakses di <xliff:g id="DEVICE">%1$s</xliff:g>. Coba di perangkat Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Setelan ini tidak dapat diakses di <xliff:g id="DEVICE">%1$s</xliff:g>. Coba di tablet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Setelan ini tidak dapat diakses di <xliff:g id="DEVICE">%1$s</xliff:g>. Coba di ponsel."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Aplikasi ini dibuat untuk Android versi lama dan mungkin tidak berfungsi sebagaimana mestinya. Coba periksa apakah ada update, atau hubungi developer."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Periksa apakah ada update"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Ada pesan baru"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Buka aplikasi SMS untuk melihat"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Izinkan akses satu kali"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Jangan izinkan"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Log perangkat merekam hal-hal yang terjadi di perangkat Anda. Aplikasi dapat menggunakan log ini untuk menemukan dan memperbaiki masalah.\n\nBeberapa log mungkin berisi info sensitif, jadi hanya izinkan aplikasi yang Anda percayai untuk mengakses semua log perangkat. \n\nJika Anda tidak mengizinkan aplikasi ini mengakses semua log perangkat, aplikasi masih dapat mengakses log-nya sendiri. Produsen perangkat masih dapat mengakses beberapa log atau info di perangkat Anda."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Log perangkat merekam hal-hal yang terjadi di perangkat Anda. Aplikasi dapat menggunakan log ini untuk menemukan dan memperbaiki masalah.\n\nBeberapa log mungkin berisi info sensitif, jadi hanya izinkan aplikasi yang Anda percayai untuk mengakses semua log perangkat. \n\nJika Anda tidak mengizinkan aplikasi ini mengakses semua log perangkat, aplikasi masih dapat mengakses log-nya sendiri. Produsen perangkat masih dapat mengakses beberapa log atau info di perangkat Anda.\n\nPelajari lebih lanjut di g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Jangan tampilkan lagi"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ingin menampilkan potongan <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 3440f18c0428..497c955beed7 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Ekki er hægt að opna þetta í <xliff:g id="DEVICE">%1$s</xliff:g>. Prófaðu það í Android TV tækinu í staðinn."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Ekki er hægt að opna þetta í <xliff:g id="DEVICE">%1$s</xliff:g>. Prófaðu það í spjaldtölvunni í staðinn."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ekki er hægt að opna þetta í <xliff:g id="DEVICE">%1$s</xliff:g>. Prófaðu það í símanum í staðinn."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Þetta forrit var hannað fyrir eldri útgáfu af Android og ekki er víst að það virki eðlilega. Athugaðu hvort uppfærslur séu í boði eða hafðu samband við þróunaraðilann."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Leita að uppfærslu"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Þú ert með ný skilaboð"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Opnaðu SMS-forritið til að skoða"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Leyfa aðgang í eitt skipti"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ekki leyfa"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Annálar tækisins skrá það sem gerist í tækinu. Forrit geta notað þessa annála til að finna og lagfæra vandamál.\n\nTilteknir annálar innihalda viðkvæmar upplýsingar og því skaltu einungis veita forritum sem þú treystir aðgang að öllum annálum tækisins. \n\nEf þú veitir þessu forriti ekki aðgang að öllum annálum tækisins hefur það áfram aðgang að eigin annálum. Framleiðandi tækisins getur þó hugsanlega opnað tiltekna annála eða upplýsingar í tækinu."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Annálar tækisins skrá það sem gerist í tækinu. Forrit geta notað þessa annála til að finna og lagfæra vandamál.\n\nTilteknir annálar innihalda viðkvæmar upplýsingar og því skaltu einungis veita forritum sem þú treystir aðgang að öllum annálum tækisins. \n\nEf þú veitir þessu forriti ekki aðgang að öllum annálum tækisins hefur það áfram aðgang að eigin annálum. Framleiðandi tækisins getur þó hugsanlega opnað tiltekna annála eða upplýsingar í tækinu.\n\nNánar á g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ekki sýna aftur"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vill sýna sneiðar úr <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Breyta"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index d5766fc30d94..d25bb063ca10 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1942,7 +1942,7 @@
<string name="app_blocked_title" msgid="7353262160455028160">"L\'app non è disponibile"</string>
<string name="app_blocked_message" msgid="542972921087873023">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> non è al momento disponibile."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> non disponibile"</string>
- <string name="app_streaming_blocked_title_for_permission_dialog" msgid="4483161748582966785">"È necessaria l\'autorizzazione"</string>
+ <string name="app_streaming_blocked_title_for_permission_dialog" msgid="4483161748582966785">"Autorizzazione necessaria"</string>
<string name="app_streaming_blocked_title_for_camera_dialog" msgid="3935701653713853065">"Fotocamera non disponibile"</string>
<string name="app_streaming_blocked_title_for_fingerprint_dialog" msgid="3516853717714141951">"Continua sul telefono"</string>
<string name="app_streaming_blocked_title_for_microphone_dialog" msgid="544822455127171206">"Microfono non disponibile"</string>
@@ -1959,7 +1959,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Non è possibile accedere a questa impostazione su <xliff:g id="DEVICE">%1$s</xliff:g>. Prova a usare il dispositivo Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Non è possibile accedere a questa impostazione su <xliff:g id="DEVICE">%1$s</xliff:g>. Prova a usare il tablet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Non è possibile accedere a questa impostazione su <xliff:g id="DEVICE">%1$s</xliff:g>. Prova a usare il telefono."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Questa app è stata realizzata per una versione precedente di Android e potrebbe non funzionare correttamente. Prova a verificare la disponibilità di aggiornamenti o contatta lo sviluppatore."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Questa app è stata progettata per una versione precedente di Android. Potrebbe non funzionare correttamente e non include le protezioni della sicurezza e della privacy più recenti. Verifica la presenza di un aggiornamento o contatta lo sviluppatore dell\'app."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Cerca aggiornamenti"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Hai nuovi messaggi"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Apri l\'app SMS per la visualizzazione"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 500a3a21e4a8..a1373c31877b 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1960,7 +1960,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"‏אי אפשר לגשת להגדרה הזו במכשיר <xliff:g id="DEVICE">%1$s</xliff:g>. במקום זאת, יש לנסות במכשיר Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"אי אפשר לגשת להגדרה הזו במכשיר <xliff:g id="DEVICE">%1$s</xliff:g>. במקום זאת, יש לנסות בטאבלט."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"אי אפשר לגשת להגדרה הזו במכשיר <xliff:g id="DEVICE">%1$s</xliff:g>. במקום זאת, אפשר לנסות בטלפון."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏האפליקציה הזו עוצבה לגרסה ישנה יותר של Android וייתכן שלא תפעל כראוי. ניתן לבדוק אם יש עדכונים או ליצור קשר עם המפתח."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"‏האפליקציה הזו תוכננה לגרסה ישנה יותר של Android. האפליקציה לא כוללת את אמצעי ההגנה האחרונים לאבטחה ופרטיות, ועלולה לא לעבוד כראוי. כדאי לבדוק אם יש עדכון או ליצור קשר עם מפתח האפליקציה."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"יש עדכון חדש?"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"יש לך הודעות חדשות"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"‏יש לפתוח את אפליקציית ה-SMS כדי להציג"</string>
@@ -2053,8 +2053,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"הרשאת גישה חד-פעמית"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"אין אישור"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"ביומני המכשיר מתועדת הפעילות במכשיר. האפליקציות יכולות להשתמש ביומנים האלה כדי למצוא בעיות ולפתור אותן.\n\nהמידע בחלק מהיומנים יכול להיות רגיש, לכן יש לתת הרשאת גישה לכל יומני המכשיר רק לאפליקציות מהימנות. \n\nגם אם האפליקציה הזו לא תקבל הרשאת גישה לכל יומני המכשיר, היא תוכל לגשת ליומנים שלה. יכול להיות שליצרן המכשיר עדיין תהיה גישה לחלק מהיומנים או למידע במכשיר שלך."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"‏ביומני המכשיר מתועדת הפעילות במכשיר. האפליקציות יכולות להשתמש ביומנים האלה כדי למצוא בעיות ולפתור אותן.\n\nהמידע בחלק מהיומנים יכול להיות רגיש, לכן יש לתת הרשאת גישה לכל היומנים של המכשיר רק לאפליקציות שסומכים עליהן. \n\nגם אם האפליקציה הזו לא תקבל הרשאת גישה לכל יומני המכשיר, היא תוכל לגשת ליומנים שלה. יכול להיות שליצרן המכשיר עדיין תהיה גישה לחלק מהיומנים או למידע במכשיר שלך.\n\nמידע נוסף זמין בכתובת g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"אין להציג שוב"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> רוצה להציג חלקים מ-<xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"עריכה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 9ebe2661f671..ed55a7fa8eb8 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"<xliff:g id="DEVICE">%1$s</xliff:g> からはアクセスできません。Android TV デバイスでのアクセスをお試しください。"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"<xliff:g id="DEVICE">%1$s</xliff:g> からはアクセスできません。タブレットでのアクセスをお試しください。"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"<xliff:g id="DEVICE">%1$s</xliff:g> からはアクセスできません。スマートフォンでのアクセスをお試しください。"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"このアプリは以前のバージョンの Android 用に作成されており、正常に動作しない可能性があります。アップデートを確認するか、デベロッパーにお問い合わせください。"</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"このアプリは Android の以前のバージョンを対象としているため、正しく動作しない可能性があります。最新のセキュリティ保護やプライバシー保護は組み込まれていません。アップデートをご確認いただくか、アプリのデベロッパーにお問い合わせください。"</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"アップデートを確認"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"新着メッセージがあります"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"表示するには SMS アプリを開きます"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 61b6d2df4d92..0b06d7c5ebef 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"ამჟამად ამ აპზე თქვენი <xliff:g id="DEVICE">%1$s</xliff:g>-დან წვდომა შეუძლებელია. ცადეთ Android TV მოწყობილობიდან."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"ამჟამად ამ აპზე თქვენი <xliff:g id="DEVICE">%1$s</xliff:g>-დან წვდომა შეუძლებელია. ცადეთ ტაბლეტიდან."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"ამჟამად ამ აპზე თქვენი <xliff:g id="DEVICE">%1$s</xliff:g>-დან წვდომა შეუძლებელია. ცადეთ ტელეფონიდან."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ეს აპი Android-ის ძველი ვერსიისთვის შეიქმნა და შესაძლოა სათანადოდ არ მუშაობდეს. გადაამოწმეთ განახლებები ან დაუკავშირდით დეველოპერს."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"ეს აპი Android-ის ძველი ვერსიისთვის შეიქმნა. ის შესაძლოა არ მიშაობდეს სწორად და არ შეიცავდეს უსაფრთხოებისა და კონფიდენციალურობის უახლეს დაცვას. შეამოწმეთ განახლება, ან დაუკავშირდით აპის დეველოპერს."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"განახლების შემოწმება"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"თქვენ ახალი შეტყობინებები გაქვთ"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"სანახავად, გახსენით SMS აპი"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 1bce8fe19772..922ecd8e4b5b 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Бұған <xliff:g id="DEVICE">%1$s</xliff:g> құрылғысынан кіру мүмкін емес. Оның орнына Android TV құрылғысын пайдаланып көріңіз."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Бұған <xliff:g id="DEVICE">%1$s</xliff:g> құрылғысынан кіру мүмкін емес. Оның орнына планшетті пайдаланып көріңіз."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Бұған <xliff:g id="DEVICE">%1$s</xliff:g> құрылғысынан кіру мүмкін емес. Оның орнына телефонды пайдаланып көріңіз."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Қолданба Android жүйесінің ескі нұсқасына арналған және дұрыс жұмыс істемеуі мүмкін. Жаңартылған нұсқаны тексеріңіз немесе әзірлеушіге хабарласыңыз."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Жаңарту бар-жоғын тексеру"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Сізде жаңа хабарлар бар"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Көру үшін SMS қолданбасын ашыңыз"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Бір реттік пайдалану рұқсатын беру"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Рұқсат бермеу"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Журналдарға құрылғыда не болып жатқаны жазылады. Қолданбалар осы журналдарды қате тауып, түзету үшін пайдаланады.\n\nКейбір журналдарда құпия ақпарат болуы мүмкін. Сондықтан барлық құрылғының журналын пайдалану рұқсаты тек сенімді қолданбаларға берілуі керек. \n\nБұл қолданбаға барлық құрылғының журналын пайдалануға рұқсат бермесеңіз де, ол өзінің журналдарын пайдалана береді. Құрылғы өндірушісі де құрылғыдағы кейбір журналдарды немесе ақпаратты пайдалануы мүмкін."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Журналдарға құрылғыда не болып жатқаны жазылады. Қолданбалар осы журналдарды қате тауып, түзету үшін пайдаланады.\n\nКейбір журналдарда құпия ақпарат болуы мүмкін. Сондықтан барлық құрылғының журналын пайдалану рұқсаты тек сенімді қолданбаларға берілуі керек. \n\nБұл қолданбаға барлық құрылғының журналын пайдалануға рұқсат бермесеңіз де, ол өзінің журналдарын пайдалана береді. Құрылғы өндірушісі де құрылғыдағы кейбір журналдарды немесе ақпаратты пайдалануы мүмкін.\n\nТолық ақпарат: g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Қайта көрсетілмесін"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> қолданбасы <xliff:g id="APP_2">%2$s</xliff:g> қолданбасының үзінділерін көрсеткісі келеді"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Өзгерту"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index a4913c339ec0..2fada731b553 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"មិនអាច​ចូលប្រើប្រាស់​កម្មវិធី​នេះ​នៅលើ <xliff:g id="DEVICE">%1$s</xliff:g> របស់អ្នកបាន​ទេ។ សូមសាកល្បងប្រើ​នៅលើ​ឧបករណ៍ Android TV របស់អ្នក​ជំនួសវិញ។"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"មិនអាច​ចូលប្រើប្រាស់​កម្មវិធី​នេះ​នៅលើ <xliff:g id="DEVICE">%1$s</xliff:g> របស់អ្នកបាន​ទេ។ សូមសាកល្បងប្រើ​នៅលើ​ថេប្លេត​របស់អ្នក​ជំនួសវិញ។"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"មិនអាច​ចូលប្រើប្រាស់​កម្មវិធី​នេះ​នៅលើ <xliff:g id="DEVICE">%1$s</xliff:g> របស់អ្នកបាន​ទេ។ សូមសាកល្បងប្រើ​នៅលើ​ទូរសព្ទរបស់អ្នក​ជំនួសវិញ។"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"កម្មវិធី​នេះ​ត្រូវបាន​បង្កើត​ឡើង​សម្រាប់​កំណែ​ប្រព័ន្ធ​ប្រតិបត្តិការ Android ចាស់ ហើយ​វាអាច​ដំណើរការ​ខុសប្រក្រតី។ សូម​សាកល្បង​ពិនិត្យមើល​កំណែ​ថ្មី ឬ​ទាក់ទង​ទៅអ្នក​អភិវឌ្ឍន៍។"</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"កម្មវិធីនេះ​ត្រូវបានបង្កើតឡើង​សម្រាប់​កំណែ Android ចាស់​ជាងនេះ។ កម្មវិធីនេះអាចមិនដំណើរការបានត្រឹមត្រូវ និងមិនរួមបញ្ចូលការការពារឯកជនភាព និងសុវត្ថិភាពចុងក្រោយបំផុតទេ។ ពិនិត្យរកមើលកំណែថ្មី ឬទាក់ទងទៅអ្នកអភិវឌ្ឍន៍កម្មវិធី។"</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"រក​មើល​កំណែ​ថ្មី"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"អ្នកមានសារថ្មី"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"បើកកម្មវិធីសារ SMS ដើម្បីមើល"</string>
@@ -2051,8 +2051,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"អនុញ្ញាតឱ្យចូលប្រើ​ម្ដង"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"មិនអនុញ្ញាត"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"កំណត់ហេតុឧបករណ៍កត់ត្រាអ្វីដែលកើតឡើងនៅលើឧបករណ៍របស់អ្នក។ កម្មវិធីអាចប្រើកំណត់ហេតុទាំងនេះដើម្បីស្វែងរក និងដោះស្រាយបញ្ហាបាន។\n\nកំណត់ហេតុមួយចំនួនអាចមានព័ត៌មានរសើប ដូច្នេះគួរអនុញ្ញាតឱ្យចូលប្រើកំណត់ហេតុឧបករណ៍ទាំងអស់សម្រាប់តែកម្មវិធីដែលអ្នកទុកចិត្តប៉ុណ្ណោះ។ \n\nប្រសិនបើអ្នកមិនអនុញ្ញាតឱ្យកម្មវិធីនេះចូលប្រើកំណត់ហេតុឧបករណ៍ទាំងអស់ទេ វានៅតែអាចចូលប្រើកំណត់ហេតុរបស់វាផ្ទាល់បាន។ ក្រុមហ៊ុន​ផលិត​ឧបករណ៍របស់អ្នក​ប្រហែលជា​នៅតែអាចចូលប្រើ​កំណត់ហេតុ ឬព័ត៌មានមួយចំនួន​នៅលើឧបករណ៍​របស់អ្នក​បានដដែល។"</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"កំណត់ហេតុឧបករណ៍កត់ត្រាអ្វីដែលកើតឡើងនៅលើឧបករណ៍របស់អ្នក។ កម្មវិធីអាចប្រើកំណត់ហេតុទាំងនេះដើម្បីស្វែងរក និងដោះស្រាយបញ្ហាបាន។\n\nកំណត់ហេតុមួយចំនួនអាចមានព័ត៌មានរសើប ដូច្នេះគួរអនុញ្ញាតឱ្យចូលប្រើកំណត់ហេតុឧបករណ៍ទាំងអស់សម្រាប់តែកម្មវិធីដែលអ្នកទុកចិត្តប៉ុណ្ណោះ។ \n\nប្រសិនបើអ្នកមិនអនុញ្ញាតឱ្យកម្មវិធីនេះចូលប្រើកំណត់ហេតុឧបករណ៍ទាំងអស់ទេ វានៅតែអាចចូលប្រើកំណត់ហេតុរបស់វាផ្ទាល់បាន។ ក្រុមហ៊ុន​ផលិត​ឧបករណ៍របស់អ្នក​ប្រហែលជា​នៅតែអាចចូលប្រើ​កំណត់ហេតុ ឬព័ត៌មានមួយចំនួន​នៅលើឧបករណ៍​របស់អ្នក​បានដដែល។\n\nស្វែងយល់បន្ថែមតាមរយៈ g.co/android/devicelogs។"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"កុំ​បង្ហាញ​ម្ដង​ទៀត"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ចង់​បង្ហាញ​ស្ថិតិ​ប្រើប្រាស់​របស់ <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"កែ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index ac8fb7e7a852..eb7c7d0a22a7 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"ನಿಮ್ಮ <xliff:g id="DEVICE">%1$s</xliff:g> ನಲ್ಲಿ ಇದನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅದರ ಬದಲು ನಿಮ್ಮ Android TV ಸಾಧನದಲ್ಲಿ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"ನಿಮ್ಮ <xliff:g id="DEVICE">%1$s</xliff:g> ನಲ್ಲಿ ಇದನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅದರ ಬದಲು ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್‌ನಲ್ಲಿ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"ನಿಮ್ಮ <xliff:g id="DEVICE">%1$s</xliff:g> ನಲ್ಲಿ ಇದನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅದರ ಬದಲು ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು Android ನ ಹಳೆಯ ಆವೃತ್ತಿಗೆ ರಚಿಸಲಾಗಿದೆ ಮತ್ತು ಸರಿಯಾಗಿ ಕೆಲಸ ಮಾಡದಿರಬಹುದು. ಅಪ್‌ಡೇಟ್‌ಗಳನ್ನು ಪರಿಶೀಲಿಸಲು ಪ್ರಯತ್ನಿಸಿ ಅಥವಾ ಡೆವಲಪರ್ ಅನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ಅಪ್‌ಡೇಟ್‌ಗಾಗಿ ಪರಿಶೀಲಿಸಿ"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"ನೀವು ಹೊಸ ಸಂದೇಶಗಳನ್ನು ಹೊಂದಿರುವಿರಿ"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"ವೀಕ್ಷಿಸಲು SMS ಅಪ್ಲಿಕೇಶನ್ ತೆರೆಯಿರಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 75bfbe432430..9b53e065447d 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"<xliff:g id="DEVICE">%1$s</xliff:g>에서는 액세스할 수 없습니다. 대신 Android TV 기기에서 시도해 보세요."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"<xliff:g id="DEVICE">%1$s</xliff:g>에서는 액세스할 수 없습니다. 대신 태블릿에서 시도해 보세요."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"<xliff:g id="DEVICE">%1$s</xliff:g>에서는 액세스할 수 없습니다. 대신 휴대전화에서 시도해 보세요."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"이 앱은 Android 이전 버전에 맞게 개발되었기 때문에 제대로 작동하지 않을 수 있습니다. 업데이트를 확인하거나 개발자에게 문의하세요."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"업데이트 확인"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"새 메시지 있음"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"SMS 앱을 열고 확인"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"일회성 액세스 허용"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"허용 안함"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"기기 로그에 기기에서 발생한 상황이 기록됩니다. 앱은 문제를 찾고 해결하는 데 이 로그를 사용할 수 있습니다.\n\n일부 로그는 민감한 정보를 포함할 수 있으므로 신뢰할 수 있는 앱만 모든 기기 로그에 액세스하도록 허용하세요. \n\n앱에 전체 기기 로그에 대한 액세스 권한을 부여하지 않아도 앱이 자체 로그에는 액세스할 수 있습니다. 기기 제조업체에서 일부 로그 또는 기기 내 정보에 액세스할 수도 있습니다."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"기기 로그에는 기기에서 발생한 상황이 기록됩니다. 앱은 문제를 찾고 해결하는 데 이 로그를 사용할 수 있습니다.\n\n일부 로그에 민감한 정보가 포함될 수 있으므로 신뢰할 수 있는 앱만 모든 기기 로그에 액세스하도록 허용하세요. \n\n앱에 전체 기기 로그에 대한 액세스 권한을 부여하지 않아도, 앱이 자체 로그에는 액세스할 수 있습니다. 기기 제조업체에서 기기 내 일부 로그 또는 정보에 액세스할 수도 있습니다.\n\ng.co/android/devicelogs에서 자세히 알아보세요."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"다시 표시 안함"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>에서 <xliff:g id="APP_2">%2$s</xliff:g>의 슬라이스를 표시하려고 합니다"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"수정"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 68be90451e7f..ad01dafc9de2 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Буга <xliff:g id="DEVICE">%1$s</xliff:g> түзмөгүңүздөн кире албайсыз. Android TV түзмөгүңүздөн аракет кылып көрүңүз."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Буга <xliff:g id="DEVICE">%1$s</xliff:g> түзмөгүңүздөн кире албайсыз. Планшетиңизден кирип көрүңүз."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Буга <xliff:g id="DEVICE">%1$s</xliff:g> түзмөгүңүздөн кире албайсыз. Анын ордуна телефондон кирип көрүңүз."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Бул колдонмо Android\'дин эски версиясы үчүн иштеп чыгарылган, андыктан туура эмес иштеши мүмкүн. Жаңыртууларды издеп көрүңүз же иштеп чыгуучуга кайрылыңыз."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Бул колдонмо эски Android версиясы үчүн түзүлгөн. Ал туура иштебеши мүмкүн жана анда коопсуздукту жана купуялыкты коргоонун эң акыркы мүмкүнчүлүктөрү камтылган эмес. Жаңыртууну издеп көрүңүз же колдонмону иштеп чыгуучуга кайрылыңыз."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Жаңыртууларды текшерүү"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Сизге жаңы билдирүүлөр келди"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Көрүү үчүн SMS колдонмосун ачыңыз"</string>
@@ -2051,8 +2051,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Бир жолу жеткиликтүү кылуу"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Жок"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Түзмөктө аткарылган бардык аракеттер түзмөктүн таржымалдарында сакталып калат. Колдонмолор бул таржымалдарды колдонуп, маселелерди оңдошот.\n\nАйрым таржымалдарда купуя маалымат болушу мүмкүн, андыктан түзмөктөгү бардык таржымалдарды ишенимдүү колдонмолорго гана пайдаланууга уруксат бериңиз. \n\nЭгер бул колдонмого түзмөктөгү айрым таржымалдарга кирүүгө тыюу салсаңыз, ал өзүнүн таржымалдарын пайдалана берет. Түзмөктү өндүрүүчү түзмөгүңүздөгү айрым таржымалдарды же маалыматты көрө берет."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Түзмөктө жасалган нерселердин баары таржымалга сактала берет. Колдонмолор анын жардамы менен көйгөйлөрдү аныктап, оңдоп турушат.\n\nАйрым таржымалдарда купуя маалымат болушу мүмкүн, андыктан ишенимдүү колдонмолорго гана түзмөктөгү бардык таржымалдарды пайдаланууга уруксат бериңиз. \n\nЭгер бул колдонмого түзмөктөгү айрым таржымалдарга кирүүгө тыюу салсаңыз, ал өзүнүн таржымалдарын пайдалана берет. Түзмөктү өндүрүүчү түзмөгүңүздөгү айрым таржымалдарды же маалыматты көрө берет.\n\nКеңири маалымат: g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Экинчи көрүнбөсүн"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> колдонмосу <xliff:g id="APP_2">%2$s</xliff:g> үлгүлөрүн көрсөткөнү жатат"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Түзөтүү"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 5865c6ed7230..02df227f37bc 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"ບໍ່ສາມາດເຂົ້າເຖິງແອັບນີ້ໄດ້ຢູ່ <xliff:g id="DEVICE">%1$s</xliff:g> ຂອງທ່ານ. ກະລຸນາລອງໃຊ້ຢູ່ອຸປະກອນ Android TV ຂອງທ່ານແທນ."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"ບໍ່ສາມາດເຂົ້າເຖິງແອັບນີ້ໄດ້ຢູ່ <xliff:g id="DEVICE">%1$s</xliff:g> ຂອງທ່ານ. ກະລຸນາລອງຢູ່ແທັບເລັດຂອງທ່ານແທນ."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"ບໍ່ສາມາດເຂົ້າເຖິງແອັບນີ້ໄດ້ຢູ່ <xliff:g id="DEVICE">%1$s</xliff:g> ຂອງທ່ານ. ກະລຸນາລອງຢູ່ໂທລະສັບຂອງທ່ານແທນ."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ແອັບນີ້ຖືກສ້າງຂຶ້ນສຳລັບ Android ເວີຊັນທີ່ເກົ່າກວ່າ ແລະ ອາດເຮັດວຽກໄດ້ບໍ່ປົກກະຕິ. ໃຫ້ລອງກວດສອບເບິ່ງອັບເດດ ຫຼື ຕິດຕໍ່ຜູ້ພັດທະນາ."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"ແອັບນີ້ສ້າງຂຶ້ນສຳລັບ Android ເວີຊັນເກົ່າ. ມັນອາດເຮັດວຽກບໍ່ຖືກຕ້ອງ ແລະ ຮວມທັງບໍ່ມີຄວາມປອດໄພ ແລະ ການປ້ອງກັນຄວາມເປັນສ່ວນຕົວຫຼ້າສຸດ. ກວດສອບເພື່ອອັບເດດ ຫຼື ຕິດຕໍ່ນັກພັດທະນາແອັບ."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ກວດເບິ່ງອັບເດດ"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"ທ່ານມີຂໍ້ຄວາມໃໝ່"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"ເປີດແອັບ SMS ເພື່ອເບິ່ງ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index a62ff4f3a5fa..4543ef621fb7 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1960,7 +1960,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Nepavyksta pasiekti nuotolinio įrenginio iš jūsų „<xliff:g id="DEVICE">%1$s</xliff:g>“. Pabandykite naudoti „Android TV“ įrenginį."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Nepavyksta pasiekti nuotolinio įrenginio iš jūsų „<xliff:g id="DEVICE">%1$s</xliff:g>“. Pabandykite naudoti planšetinį kompiuterį."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Nepavyksta pasiekti nuotolinio įrenginio iš jūsų „<xliff:g id="DEVICE">%1$s</xliff:g>“. Pabandykite naudoti telefoną."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ši programa sukurta naudoti senesnės versijos sistemoje „Android“ ir gali tinkamai neveikti. Pabandykite patikrinti, ar yra naujinių, arba susisiekite su kūrėju."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ši programa sukurta senesnės versijos „Android“. Ji gali tinkamai neveikti ir joje nėra naujausių saugos ir privatumo apsaugos priemonių. Patikrinkite, ar yra naujinių, arba susisiekite su programos kūrėju."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Tikrinti, ar yra naujinių"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Turite naujų pranešimų"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Atidaryti SMS programą, norint peržiūrėti"</string>
@@ -2053,8 +2053,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Leisti vienkartinę prieigą"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Neleisti"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Įrenginyje įrašoma, kas įvyksta jūsų įrenginyje. Programos gali naudoti šiuos žurnalus, kad surastų ir išspręstų problemas.\n\nKai kuriuose žurnaluose gali būti neskelbtinos informacijos, todėl visus įrenginio žurnalus leiskite pasiekti tik programoms, kuriomis pasitikite. \n\nJei neleisite šiai programai pasiekti visų įrenginio žurnalų, ji vis tiek galės pasiekti savo žurnalus. Įrenginio gamintojui vis tiek gali būti leidžiama pasiekti tam tikrus žurnalus ar informaciją jūsų įrenginyje."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Įrenginyje įrašoma, kas jame įvyksta. Programos gali naudoti šiuos žurnalus, kai reikia surasti ir išspręsti problemas.\n\nKai kuriuose žurnaluose gali būti neskelbtinos informacijos, todėl visus įrenginio žurnalus leiskite pasiekti tik programoms, kuriomis pasitikite. \n\nJei neleisite šiai programai pasiekti visų įrenginio žurnalų, ji vis tiek galės pasiekti savo žurnalus. Įrenginio gamintojui vis tiek gali būti leidžiama pasiekti tam tikrus žurnalus ar informaciją jūsų įrenginyje.\n\nSužinokite daugiau adresu g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Daugiau neberodyti"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"„<xliff:g id="APP_0">%1$s</xliff:g>“ nori rodyti „<xliff:g id="APP_2">%2$s</xliff:g>“ fragmentus"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Redaguoti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 172e8a5bbdbf..fa001fca39dc 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1958,8 +1958,9 @@
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Šī lietotne pieprasa papildu drošību. Mēģiniet tai piekļūt savā tālrunī."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Šajā ierīcē (<xliff:g id="DEVICE">%1$s</xliff:g>) nevar piekļūt tālvadībai. Mēģiniet tai piekļūt savā Android TV ierīcē."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Šajā ierīcē (<xliff:g id="DEVICE">%1$s</xliff:g>) nevar piekļūt tālvadībai. Mēģiniet tai piekļūt savā planšetdatorā."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Šajā ierīcē (<xliff:g id="DEVICE">%1$s</xliff:g>) nevar piekļūt tālvadībai. Mēģiniet tai piekļūt savā tālrunī."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Šī lietotne tika izstrādāta vecākai Android versijai un var nedarboties pareizi. Meklējiet atjauninājumus vai sazinieties ar izstrādātāju."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ierīcē <xliff:g id="DEVICE">%1$s</xliff:g> nevar piekļūt šai funkcijai. Mēģiniet tai piekļūt tālrunī."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Meklēt atjauninājumu"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Jums ir jaunas īsziņas."</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Lai skatītu, atveriet īsziņu lietotni."</string>
@@ -2052,8 +2053,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Atļaut vienreizēju piekļuvi"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Neatļaut"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Ierīces žurnālos tiek reģistrēti ierīces procesi un notikumi. Lietotņu izstrādātāji var izmantot šos žurnālus, lai atrastu un izlabotu problēmas savās lietotnēs.\n\nDažos žurnālos var būt ietverta sensitīva informācija, tāpēc atļaujiet tikai uzticamām lietotnēm piekļūt visiem ierīces žurnāliem. \n\nJa neatļausiet šai lietotnei piekļūt visiem ierīces žurnāliem, lietotnes izstrādātājs joprojām varēs piekļūt pašas lietotnes žurnāliem. Iespējams, ierīces ražotājs joprojām varēs piekļūt noteiktiem žurnāliem vai informācijai jūsu ierīcē."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Ierīces žurnālos tiek reģistrēti ierīces procesi un notikumi. Lietotņu izstrādātāji var izmantot šos žurnālus, lai atrastu un izlabotu problēmas savās lietotnēs.\n\nDažos žurnālos var būt ietverta sensitīva informācija, tāpēc atļaujiet tikai uzticamām lietotnēm piekļūt visiem ierīces žurnāliem. \n\nJa neatļausiet šai lietotnei piekļūt visiem ierīces žurnāliem, lietotnes izstrādātājs joprojām varēs piekļūt pašas lietotnes žurnāliem. Iespējams, ierīces ražotājs joprojām varēs piekļūt noteiktiem žurnāliem vai informācijai jūsu ierīcē.\n\nŠeit varat uzzināt vairāk: g.co/android/devicelogs"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Vairs nerādīt"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Lietotne <xliff:g id="APP_0">%1$s</xliff:g> vēlas rādīt lietotnes <xliff:g id="APP_2">%2$s</xliff:g> sadaļas"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Rediģēt"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index b4863dffd47e..0af4cdd90cec 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Ова не може да се отвори на <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на вашиот Android TV како алтернатива."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Ова не може да се отвори на <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на вашиот таблет како алтернатива."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ова не може да се отвори на <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на вашиот телефон како алтернатива."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Апликацијава е создадена за постара верзија на Android и може да не функционира правилно. Проверете за ажурирања или контактирајте со програмерот."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Оваа апликација е создадена за постара верзија на Android. Можеби нема да работи правилно и не ги вклучува најновите мерки за заштита на безбедноста и приватноста. Проверете дали има ажурирање или контактирајте со програмерот на апликацијата."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Проверка за ажурирање"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Имате нови пораки"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Отворете ја апликацијата за SMS за приказ"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 14a73fde17c6..04d2d60a6122 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"നിങ്ങളുടെ <xliff:g id="DEVICE">%1$s</xliff:g> ഉപകരണത്തിൽ ഇത് ആക്‌സസ് ചെയ്യാനാകില്ല. പകരം നിങ്ങളുടെ Android TV ഉപകരണത്തിൽ ശ്രമിച്ച് നോക്കൂ."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"നിങ്ങളുടെ <xliff:g id="DEVICE">%1$s</xliff:g> ഉപകരണത്തിൽ ഇത് ആക്‌സസ് ചെയ്യാനാകില്ല. പകരം നിങ്ങളുടെ ടാബ്‌ലെറ്റിൽ ശ്രമിച്ച് നോക്കൂ."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"നിങ്ങളുടെ <xliff:g id="DEVICE">%1$s</xliff:g> ഉപകരണത്തിൽ ഇത് ആക്‌സസ് ചെയ്യാനാകില്ല. പകരം നിങ്ങളുടെ ഫോണിൽ ശ്രമിച്ച് നോക്കൂ."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ഈ ആപ്പ് Android-ന്റെ പഴയ പതിപ്പിനായി നിർമ്മിച്ചിരിക്കുന്നതിനാൽ ശരിയായി പ്രവർത്തിച്ചേക്കില്ല. അപ്‌ഡേറ്റിനായി പരിശോധിക്കുക, അല്ലെങ്കിൽ ഡെവലപ്പറുമായി ബന്ധപ്പെടുക."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"അപ്‌ഡേറ്റിനായി പരിശോധിക്കുക"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"നിങ്ങൾക്ക് പുതിയ സന്ദേശങ്ങൾ ഉണ്ട്"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"കാണുന്നതിന് SMS ആപ്പ് തുറക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index cdf68a618658..4e8c314dbe7b 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Үүнд таны <xliff:g id="DEVICE">%1$s</xliff:g>-с хандах боломжгүй. Оронд нь Android TV төхөөрөмж дээрээ туршиж үзнэ үү."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Үүнд таны <xliff:g id="DEVICE">%1$s</xliff:g>-с хандах боломжгүй. Оронд нь таблет дээрээ туршиж үзнэ үү."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Үүнд таны <xliff:g id="DEVICE">%1$s</xliff:g>-с хандах боломжгүй. Оронд нь утсан дээрээ туршиж үзнэ үү."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Энэ аппыг Андройдын хуучин хувилбарт зориулсан бөгөөд буруу ажиллаж болзошгүй. Шинэчлэлтийг шалгаж эсвэл хөгжүүлэгчтэй холбогдоно уу."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Энэ аппыг Android-н хуучин хувилбарт зориулж бүтээсэн. Энэ нь зохих ёсоор ажиллахгүй байж магадгүй бөгөөд хамгийн сүүлийн үеийн аюулгүй байдал болон нууцлалын хамгаалалтыг агуулдаггүй. Шинэчлэлт байгаа эсэхийг шалгах эсвэл аппын хөгжүүлэгчтэй холбогдоно уу."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Шинэчлэлтийг шалгах"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Танд шинэ мессежүүд байна"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Үзэхийн тулд SMS аппыг нээх"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index ff8d82fd404d..678f6eea2260 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"हे तुमच्या <xliff:g id="DEVICE">%1$s</xliff:g> वर अ‍ॅक्सेस केले जाऊ शकत नाही. त्याऐवजी तुमच्या Android TV डिव्हाइसवर अ‍ॅक्सेस करून पहा."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"हे तुमच्या <xliff:g id="DEVICE">%1$s</xliff:g> वर अ‍ॅक्सेस केले जाऊ शकत नाही. त्याऐवजी तुमच्या टॅबलेटवर अ‍ॅक्सेस करून पहा."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"हे तुमच्या <xliff:g id="DEVICE">%1$s</xliff:g> वर अ‍ॅक्सेस केले जाऊ शकत नाही. त्याऐवजी तुमच्या फोनवर अ‍ॅक्सेस करून पहा."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"हे अ‍ॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"अपडेटसाठी तपासा"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"आपल्याकडे नवीन मेसेज आहेत"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"पाहण्‍यासाठी SMS अ‍ॅप उघडा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 8566afb4bb9c..9a6ee3bee0da 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Apl ini tidak boleh diakses pada <xliff:g id="DEVICE">%1$s</xliff:g> anda. Cuba pada peranti Android TV anda."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Apl ini tidak boleh diakses pada <xliff:g id="DEVICE">%1$s</xliff:g> anda. Cuba pada tablet anda."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Apl ini tidak boleh diakses pada <xliff:g id="DEVICE">%1$s</xliff:g> anda. Cuba pada telefon anda."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Apl ini dibina untuk versi Android yang lebih lama dan mungkin tidak berfungsi dengan betul. Cuba semak kemas kini atau hubungi pembangun."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Apl ini dibina untuk versi Android yang lebih lama. Apl ini mungkin tidak berfungsi dengan betul dan tidak termasuk perlindungan keselamatan dan privasi yang terkini. Semak kemas kini atau hubungi pembangun apl."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Semak kemaskinian"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Anda mempunyai mesej baharu"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Buka apl SMS untuk melihat"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 5a9f2ac769c3..0dfc449238cb 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"၎င်းကို သင်၏ <xliff:g id="DEVICE">%1$s</xliff:g> တွင် သုံး၍မရပါ။ Android TV စက်တွင် စမ်းကြည့်ပါ။"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"၎င်းကို သင်၏ <xliff:g id="DEVICE">%1$s</xliff:g> တွင် သုံး၍မရပါ။ တက်ဘလက်တွင် စမ်းကြည့်ပါ။"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"၎င်းကို သင်၏ <xliff:g id="DEVICE">%1$s</xliff:g> တွင် သုံး၍မရပါ။ ဖုန်းတွင် စမ်းကြည့်ပါ။"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ဤအက်ပ်ကို Android ဗားရှင်းဟောင်းအတွက် ပြုလုပ်ထားခြင်းဖြစ်ပြီး ပုံမှန်အလုပ်မလုပ်နိုင်ပါ။ အပ်ဒိတ်များအတွက် ရှာကြည့်ပါ သို့မဟုတ် ဆော့ဖ်ဝဲအင်ဂျင်နီယာကို ဆက်သွယ်ပါ။"</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"အပ်ဒိတ်စစ်ရန်"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"သင့်ထံတွင် စာအသစ်များရောက်နေသည်"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"ကြည့်ရှုရန် SMS အက်ပ်ကိုဖွင့်ပါ"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"တစ်ခါသုံး ဝင်ခွင့်ပေးရန်"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"ခွင့်မပြုပါ"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"သင့်စက်ရှိ အဖြစ်အပျက်များကို စက်မှတ်တမ်းများက မှတ်တမ်းတင်သည်။ အက်ပ်များက ပြဿနာများ ရှာဖွေပြီးဖြေရှင်းရန် ဤမှတ်တမ်းများကို သုံးနိုင်သည်။\n\nအချို့မှတ်တမ်းများတွင် သတိထားရမည့်အချက်အလက်များ ပါဝင်နိုင်သဖြင့် စက်မှတ်တမ်းအားလုံးကို ယုံကြည်ရသည့် အက်ပ်များကိုသာ သုံးခွင့်ပြုပါ။ \n\nဤအက်ပ်ကို စက်မှတ်တမ်းအားလုံး သုံးခွင့်မပြုသော်လည်း ၎င်းက ၎င်း၏ကိုယ်ပိုင်မှတ်တမ်းကို သုံးနိုင်ဆဲဖြစ်သည်။ သင့်စက်ရှိ အချို့မှတ်တမ်းများ (သို့) အချက်အလက်များကို သင့်စက်ထုတ်လုပ်သူက သုံးနိုင်ပါသေးသည်။"</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"သင့်စက်ရှိ အဖြစ်အပျက်များကို စက်မှတ်တမ်းများက မှတ်တမ်းတင်သည်။ အက်ပ်များက ပြဿနာများ ရှာဖွေပြီးဖြေရှင်းရန် ဤမှတ်တမ်းများကို သုံးနိုင်သည်။\n\nအချို့မှတ်တမ်းများတွင် သတိထားရမည့်အချက်အလက်များ ပါဝင်နိုင်သဖြင့် ယုံကြည်ရသည့် အက်ပ်များကိုသာ စက်မှတ်တမ်းအားလုံး သုံးခွင့်ပြုပါ။ \n\nဤအက်ပ်ကို စက်မှတ်တမ်းအားလုံး သုံးခွင့်မပြုသော်လည်း ၎င်းက ကိုယ်ပိုင်မှတ်တမ်းများ သုံးနိုင်သေးသည်။ သင့်စက်ရှိ မှတ်တမ်း (သို့) အချက်အလက်အချို့ကို စက်ထုတ်လုပ်သူက သုံးနိုင်သေးသည်။\n\ng.co/android/devicelogs တွင် ပိုမိုလေ့လာပါ။"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"နောက်ထပ်မပြပါနှင့်"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> သည် <xliff:g id="APP_2">%2$s</xliff:g> ၏အချပ်များကို ပြသလိုသည်"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"တည်းဖြတ်ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 0cf718bfe72e..84e1e6afc315 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -119,7 +119,7 @@
<string name="roamingTextSearching" msgid="5323235489657753486">"Leter etter tjeneste"</string>
<string name="wfcRegErrorTitle" msgid="3193072971584858020">"Kunne ikke konfigurere wifi-anrop"</string>
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="468830943567116703">"For å ringe og sende meldinger over Wi-Fi, må du først be operatøren om å konfigurere denne tjenesten. Deretter slår du på wifi-anrop igjen fra Innstillinger. (Feilkode: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+ <item msgid="468830943567116703">"For å ringe og sende meldinger over Wifi, må du først be operatøren om å konfigurere denne tjenesten. Deretter slår du på wifi-anrop igjen fra Innstillinger. (Feilkode: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="4795145070505729156">"Problem med å registrere wifi-anrop med operatøren din: <xliff:g id="CODE">%1$s</xliff:g>"</item>
@@ -130,7 +130,7 @@
<string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g>-Wifi-anrop"</string>
<string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"WLAN-anrop"</string>
<string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"<xliff:g id="SPN">%s</xliff:g> WLAN-anrop"</string>
- <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
+ <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> Wifi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Wifi-anrop | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="6865214948822061486">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
<string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Wifi-anrop"</string>
@@ -138,9 +138,9 @@
<string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Wifi-anrop"</string>
<string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="5626710010766902560">"Av"</string>
- <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Ring via Wi-Fi"</string>
+ <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Ring via Wifi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Ring over mobilnettverk"</string>
- <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Bare Wi-Fi"</string>
+ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Bare Wifi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
<string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g>-reserve for anrop"</string>
@@ -511,14 +511,14 @@
<string name="permdesc_changeNetworkState" msgid="649341947816898736">"Lar appen endre innstillingene for nettverkstilknytning."</string>
<string name="permlab_changeTetherState" msgid="9079611809931863861">"endre tilknytningsoppsett"</string>
<string name="permdesc_changeTetherState" msgid="3025129606422533085">"Lar appen endre innstillingene for delt nettforbindelse."</string>
- <string name="permlab_accessWifiState" msgid="5552488500317911052">"se Wi-Fi-tilkoblinger"</string>
- <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Lar appen se informasjon om Wi-Fi-nettverk, f.eks. hvorvidt Wi-Fi er aktivert og navn på tilkoblede Wi-Fi-enheter."</string>
- <string name="permlab_changeWifiState" msgid="7947824109713181554">"koble til og fra Wi-Fi"</string>
- <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Lar appen koble til og fra Wi-Fi-tilgangspunkter, og å gjøre endringer i enhetens konfigurasjon for Wi-Fi-nettverk."</string>
+ <string name="permlab_accessWifiState" msgid="5552488500317911052">"se Wifi-tilkoblinger"</string>
+ <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Lar appen se informasjon om Wifi-nettverk, f.eks. hvorvidt Wifi er aktivert og navn på tilkoblede Wifi-enheter."</string>
+ <string name="permlab_changeWifiState" msgid="7947824109713181554">"koble til og fra wifi"</string>
+ <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Lar appen koble til og fra wifi-tilgangspunkter, og å gjøre endringer i enhetens konfigurasjon for wifi-nettverk."</string>
<string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"tillate multicast for trådløse nettverk"</string>
- <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Lar appen motta pakker som sendes til alle enhetene på et Wi-Fi-nettverk ved hjelp av multikastingsadresser, Dette bruker mer strøm enn modusen uten multikasting."</string>
- <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Lar appen motta pakker som sendes til alle enhetene på et Wi-Fi-nettverk ved hjelp av multikastingsadresser, ikke bare Android TV-enheten din. Dette bruker mer strøm enn modus uten multikasting."</string>
- <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Lar appen motta pakker som sendes til alle enhetene på et Wi-Fi-nettverk ved hjelp av multikastingsadresser, Dette bruker mer strøm enn modusen uten multikasting."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Lar appen motta pakker som sendes til alle enhetene på et Wifi-nettverk ved hjelp av multikastingsadresser, Dette bruker mer strøm enn modusen uten multikasting."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Lar appen motta pakker som sendes til alle enhetene på et Wifi-nettverk ved hjelp av multikastingsadresser, ikke bare Android TV-enheten din. Dette bruker mer strøm enn modus uten multikasting."</string>
+ <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Lar appen motta pakker som sendes til alle enhetene på et Wifi-nettverk ved hjelp av multikastingsadresser, Dette bruker mer strøm enn modusen uten multikasting."</string>
<string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"endre Bluetooth-innstillinger"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Lar appen konfigurere det lokale Bluetooth-nettbrettet, samt oppdage og koble sammen med eksterne enheter."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Lar appen konfigurere Bluetooth på Android TV-enheten din samt oppdage og koble sammen med eksterne enheter."</string>
@@ -541,8 +541,8 @@
<string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Lar appen vise annonser til Bluetooth-enheter i nærheten"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"fastslå relativ posisjon mellom enheter som bruker ultrabredbånd"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"tillate at appen fastslår den relative posisjonen mellom enheter i nærheten som bruker ultrabredbånd"</string>
- <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"samhandle med Wi-Fi-enheter i nærheten"</string>
- <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lar appen annonsere, koble til og fastslå den relative posisjonen til Wi-Fi-enheter i nærheten"</string>
+ <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"samhandle med wifi-enheter i nærheten"</string>
+ <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lar appen annonsere, koble til og fastslå den relative posisjonen til wifi-enheter i nærheten"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informasjon om prioritert NFC-betalingstjeneste"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Gir appen tilgang til informasjon om prioritert NFC-betalingstjeneste, for eksempel registrerte hjelpemidler og destinasjon."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kontroller overføring av data med NFC-teknologi"</string>
@@ -1291,7 +1291,7 @@
<string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Alarmlyder"</string>
<string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Varsellyder"</string>
<string name="ringtone_unknown" msgid="5059495249862816475">"Ukjent"</string>
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Logg på Wi-Fi-nettverket"</string>
+ <string name="wifi_available_sign_in" msgid="381054692557675237">"Logg på Wifi-nettverket"</string>
<string name="network_available_sign_in" msgid="1520342291829283114">"Logg på nettverk"</string>
<!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
<skip />
@@ -1574,10 +1574,10 @@
<string name="data_usage_warning_title" msgid="9034893717078325845">"Varsel om databruk"</string>
<string name="data_usage_warning_body" msgid="1669325367188029454">"Du har brukt <xliff:g id="APP">%s</xliff:g> med data"</string>
<string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"Grensen for mobildata er nådd"</string>
- <string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Datagrensen for Wi-Fi er nådd"</string>
+ <string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Datagrensen for wifi er nådd"</string>
<string name="data_usage_limit_body" msgid="3567699582000085710">"Data er på pause i resten av syklusen"</string>
<string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"Over grensen for mobildata"</string>
- <string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"Over grensen din for Wi-Fi-data"</string>
+ <string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"Over grensen din for wifi-data"</string>
<string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Du er <xliff:g id="SIZE">%s</xliff:g> over den angitte grensen din"</string>
<string name="data_usage_restricted_title" msgid="126711424380051268">"Bakgrunnsdata er begrenset"</string>
<string name="data_usage_restricted_body" msgid="5338694433686077733">"Trykk for å fjerne begrensningen."</string>
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Dette er ikke tilgjengelig på <xliff:g id="DEVICE">%1$s</xliff:g>. Prøv på Android TV-enheten din i stedet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Dette er ikke tilgjengelig på <xliff:g id="DEVICE">%1$s</xliff:g>. Prøv på nettbrettet ditt i stedet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Dette er ikke tilgjengelig på <xliff:g id="DEVICE">%1$s</xliff:g>. Prøv på telefonen din i stedet."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Denne appen er utviklet for en eldre versjon av Android og fungerer kanskje ikke som den skal. Prøv å se etter oppdateringer, eller kontakt utvikleren."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Se etter oppdateringer"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Du har nye meldinger"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Åpne SMS-appen for å se"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 4ebbe740ac33..7ce406a55b91 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"तपाईंको <xliff:g id="DEVICE">%1$s</xliff:g> मा यो एप चलाउन मिल्दैन। बरु तपाईंको Android TV डिभाइसमा स्ट्रिम गरी हेर्नुहोस्।"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"तपाईंको <xliff:g id="DEVICE">%1$s</xliff:g> मा यो एप चलाउन मिल्दैन। बरु तपाईंको ट्याब्लेटमा स्ट्रिम गरी हेर्नुहोस्।"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"तपाईंको <xliff:g id="DEVICE">%1$s</xliff:g> मा यो एप चलाउन मिल्दैन। बरु तपाईंको फोनमा स्ट्रिम गरी हेर्नुहोस्।"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"यो एप Android को पुरानो संस्करणका लागि बनाइएको हुनाले यसले सही ढङ्गले काम नगर्न सक्छ। अद्यावधिकहरू उपलब्ध छन् वा छैनन् भनी जाँच गरी हेर्नुहोस् वा यसको विकासकर्तालाई सम्पर्क गर्नुहोस्।"</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"अपडेट उपलब्ध छ वा छैन जाँच्नुहोस्"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"तपाईंलाई नयाँ सन्देश आएको छ"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"हेर्नका लागि SMS एप खोल्नुहोस्"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"एक पटक प्रयोग गर्ने अनुमति दिनुहोस्"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"अनुमति नदिनुहोस्"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"डिभाइसका लगले तपाईंको डिभाइसमा भएका विभिन्न गतिविधिको अभिलेख राख्छ। एपहरू यी लगका आधारमा समस्या पत्ता लगाउन र तिनको समाधान गर्न सक्छन्।\n\nकेही लगहरूमा संवेदनशील जानकारी समावेश हुन सक्ने भएकाले आफूले भरोसा गर्ने एपलाई मात्र डिभाइसका सबै लग हेर्ने अनुमति दिनुहोस्। \n\nतपाईंले यो एपलाई डिभाइसका सबै लग हेर्ने अनुमति दिनुभएन भने पनि यसले आफ्नै लग भने हेर्न सक्छ। तपाईंको डिभाइसको उत्पादकले पनि तपाईंको डिभाइसमा भएका केही लग वा जानकारी हेर्न सक्ने सम्भावना हुन्छ।"</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"डिभाइसका लगले तपाईंको डिभाइसमा भएका विभिन्न गतिविधिको अभिलेख राख्छ। एपहरू यी लगका आधारमा समस्या पत्ता लगाउन र तिनको समाधान गर्न सक्छन्।\n\nकेही लगहरूमा संवेदनशील जानकारी समावेश हुन सक्ने भएकाले आफूले भरोसा गर्ने एपलाई मात्र डिभाइसका सबै लग हेर्ने अनुमति दिनुहोस्। \n\nतपाईंले यो एपलाई डिभाइसका सबै लग हेर्ने अनुमति दिनुभएन भने पनि यसले आफ्नै लग भने हेर्न सक्छ। तपाईंको डिभाइसको उत्पादकले पनि तपाईंको डिभाइसमा भएका केही लग वा जानकारी हेर्न सक्ने सम्भावना हुन्छ।\n\nतपाईं यस सम्बन्धमा थप जान्न चाहनुहुन्छ भने g.co/android/devicelogs मा जानुहोस्।"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"फेरि नदेखाइयोस्"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ले <xliff:g id="APP_2">%2$s</xliff:g> का स्लाइसहरू देखाउन चाहन्छ"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"सम्पादन गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 583b2de0a3ec..162d3e818563 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Je hebt hier geen toegang toe op je <xliff:g id="DEVICE">%1$s</xliff:g>. Probeer het in plaats daarvan op je Android TV-apparaat."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Je hebt hier geen toegang toe op je <xliff:g id="DEVICE">%1$s</xliff:g>. Probeer het in plaats daarvan op je tablet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Je hebt hier geen toegang toe op je <xliff:g id="DEVICE">%1$s</xliff:g>. Probeer het in plaats daarvan op je telefoon."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Deze app is ontwikkeld voor een oudere versie van Android en werkt mogelijk niet op de juiste manier. Controleer op updates of neem contact op met de ontwikkelaar."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Deze app is ontworpen voor een oudere versie van Android. De app werkt misschien niet goed en bevat niet de nieuwste beveiligings- en privacybeschermingsopties. Check op een update of neem contact op met de ontwikkelaar van de app."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Checken op updates"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Je hebt nieuwe berichten"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Open je sms-app om ze te bekijken"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index d7c1f51a6227..ca1bda7af41b 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"ଏହାକୁ ଆପଣଙ୍କ <xliff:g id="DEVICE">%1$s</xliff:g>ରେ ଆକ୍ସେସ କରାଯାଇପାରିବ ନାହିଁ। ଏହା ପରିବର୍ତ୍ତେ ଆପଣଙ୍କ Android TV ଡିଭାଇସରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"ଏହାକୁ ଆପଣଙ୍କ <xliff:g id="DEVICE">%1$s</xliff:g>ରେ ଆକ୍ସେସ କରାଯାଇପାରିବ ନାହିଁ। ଏହା ପରିବର୍ତ୍ତେ ଆପଣଙ୍କ ଟାବଲେଟରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"ଏହାକୁ ଆପଣଙ୍କ <xliff:g id="DEVICE">%1$s</xliff:g>ରେ ଆକ୍ସେସ କରାଯାଇପାରିବ ନାହିଁ। ଏହା ପରିବର୍ତ୍ତେ ଆପଣଙ୍କ ଫୋନରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ଏହି ଆପକୁ Androidର ପୁରୁଣା ସଂସ୍କରଣ ପାଇଁ ନିର୍ମାଣ କରାଯାଇଥିଲା ଏବଂ ଠିକ୍ ଭାବେ କାମ କରିନପାରେ। ଏଥିପାଇଁ ଅପଡେଟ ଅଛି କି ନାହିଁ ଯାଞ୍ଚ କରନ୍ତୁ କିମ୍ବା ଡେଭେଲପରଙ୍କ ସହିତ କଣ୍ଟାକ୍ଟ କରନ୍ତୁ।"</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ଅପଡେଟ୍‌ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"ଆପଣଙ୍କ ପାଖରେ ନୂଆ ମେସେଜ୍‍ ରହିଛି"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"ଦେଖିବା ପାଇଁ SMS ଆପ୍‍ ଖୋଲନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 3e12790587f4..438d61f1d914 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"ਤੁਹਾਡੇ <xliff:g id="DEVICE">%1$s</xliff:g> \'ਤੇ ਇਸ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਇਸਦੀ ਬਜਾਏ ਆਪਣੇ Android TV ਡੀਵਾਈਸ \'ਤੇ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"ਤੁਹਾਡੇ <xliff:g id="DEVICE">%1$s</xliff:g> \'ਤੇ ਇਸ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਇਸਦੀ ਬਜਾਏ ਆਪਣੇ ਟੈਬਲੈੱਟ \'ਤੇ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"ਤੁਹਾਡੇ <xliff:g id="DEVICE">%1$s</xliff:g> \'ਤੇ ਇਸ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਇਸਦੀ ਬਜਾਏ ਆਪਣੇ ਫ਼ੋਨ \'ਤੇ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ਇਹ ਐਪ Android ਦੇ ਕਿਸੇ ਵਧੇਰੇ ਪੁਰਾਣੇ ਵਰਜਨ ਲਈ ਬਣਾਈ ਗਈ ਸੀ ਅਤੇ ਸ਼ਾਇਦ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ। ਅੱਪਡੇਟਾਂ ਲਈ ਜਾਂਚ ਕਰੋ ਜਾਂ ਵਿਕਾਸਕਾਰ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ਅੱਪਡੇਟ ਲਈ ਜਾਂਚ ਕਰੋ"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"ਤੁਹਾਨੂੰ ਨਵੇਂ ਸੁਨੇਹੇ ਪ੍ਰਾਪਤ ਹੋਏ ਹਨ"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"ਦੇਖਣ ਲਈ SMS ਐਪ ਖੋਲ੍ਹੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 43b520ef4657..5dbe6e96fd65 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1960,7 +1960,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Nie można z tego skorzystać na urządzeniu <xliff:g id="DEVICE">%1$s</xliff:g>. Użyj urządzenia z Androidem TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Nie można z tego skorzystać na urządzeniu <xliff:g id="DEVICE">%1$s</xliff:g>. Użyj tabletu."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Nie można z tego skorzystać na urządzeniu <xliff:g id="DEVICE">%1$s</xliff:g>. Użyj telefonu."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ta aplikacja jest na starszą wersję Androida i może nie działać prawidłowo. Sprawdź dostępność aktualizacji lub skontaktuj się z programistą."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Sprawdź dostępność aktualizacji"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Masz nowe wiadomości"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Otwórz aplikację do SMS-ów, by wyświetlić wiadomość"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index a4f7da23d6a9..8635d769d9e6 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -291,7 +291,7 @@
<string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="8974401416068943888">"Modo de segurança"</string>
<string name="android_system_label" msgid="5974767339591067210">"Sistema Android"</string>
- <string name="user_owner_label" msgid="8628726904184471211">"Deslize até o perfil pessoal"</string>
+ <string name="user_owner_label" msgid="8628726904184471211">"Mudar para o perfil pessoal"</string>
<string name="managed_profile_label" msgid="7316778766973512382">"Perfil de trabalho"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"Contatos"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"acesse seus contatos"</string>
@@ -746,7 +746,7 @@
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"Permite que o proprietário use serviços da operadora. Não deve ser necessário para apps comuns."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"acessar \"Não perturbe\""</string>
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permitir que o app leia e grave a configuração \"Não perturbe\"."</string>
- <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso da permissão para visualização"</string>
+ <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"começar a usar a permissão para ver"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o sistema inicie o uso de permissão para um app. Não deve ser necessário para apps comuns."</string>
<string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"decisões de permissão da visualização inicial"</string>
<string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Autoriza o detentor a iniciar a tela para revisar as decisões de permissão. Não deve ser necessário para apps normais."</string>
@@ -1959,7 +1959,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Não é possível acessar essa configuração pelo seu <xliff:g id="DEVICE">%1$s</xliff:g>. Tente pelo dispositivo Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Não é possível acessar essa configuração pelo seu <xliff:g id="DEVICE">%1$s</xliff:g>. Tente pelo tablet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Não é possível acessar essa configuração pelo seu <xliff:g id="DEVICE">%1$s</xliff:g>. Tente pelo smartphone."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Este app foi criado para uma versão mais antiga do Android e pode não funcionar corretamente. Tente verificar se há atualizações ou entre em contato com o desenvolvedor."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Este app foi criado para uma versão mais antiga do Android. Ele pode não funcionar corretamente e não inclui as proteções de privacidade e segurança mais recentes. Verifique se há uma atualização ou entre em contato com o desenvolvedor do app."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Procurar atualizações"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Você tem mensagens novas"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Abra o app de SMS para ver"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 08b330252a87..9ae257cb923a 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -159,7 +159,7 @@
<string name="httpErrorAuth" msgid="469553140922938968">"Não foi possível autenticar."</string>
<string name="httpErrorProxyAuth" msgid="7229662162030113406">"A autenticação através do servidor proxy falhou."</string>
<string name="httpErrorConnect" msgid="3295081579893205617">"Não foi possível ligar ao servidor."</string>
- <string name="httpErrorIO" msgid="3860318696166314490">"Não foi possível comunicar com o servidor. Tente novamente mais tarde."</string>
+ <string name="httpErrorIO" msgid="3860318696166314490">"Não foi possível comunicar com o servidor. Tente mais tarde."</string>
<string name="httpErrorTimeout" msgid="7446272815190334204">"Esgotou o tempo limite da ligação ao servidor."</string>
<string name="httpErrorRedirectLoop" msgid="8455757777509512098">"A página contém demasiados redireccionamentos do servidor."</string>
<string name="httpErrorUnsupportedScheme" msgid="2664108769858966374">"O protocolo não é suportado."</string>
@@ -167,7 +167,7 @@
<string name="httpErrorBadUrl" msgid="754447723314832538">"Não foi possível abrir a página porque o URL é inválido."</string>
<string name="httpErrorFile" msgid="3400658466057744084">"Não foi possível aceder ao ficheiro."</string>
<string name="httpErrorFileNotFound" msgid="5191433324871147386">"Não foi possível localizar o ficheiro solicitado."</string>
- <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"Existem demasiados pedidos em processamento. Tente novamente mais tarde."</string>
+ <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"Existem demasiados pedidos em processamento. Tente mais tarde."</string>
<string name="notification_title" msgid="5783748077084481121">"Erro de início de sessão de <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
<string name="contentServiceSync" msgid="2341041749565687871">"Sincronização"</string>
<string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"Não é possível sincronizar"</string>
@@ -668,7 +668,7 @@
<string name="face_error_no_space" msgid="5649264057026021723">"Não pode guardar novos dados de rostos. Elimine um antigo."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Operação de rosto cancelada."</string>
<string name="face_error_user_canceled" msgid="5766472033202928373">"Desbloqueio facial cancelado pelo utilizador"</string>
- <string name="face_error_lockout" msgid="7864408714994529437">"Demasiadas tentativas. Tente novamente mais tarde."</string>
+ <string name="face_error_lockout" msgid="7864408714994529437">"Demasiadas tentativas. Tente mais tarde."</string>
<string name="face_error_lockout_permanent" msgid="3277134834042995260">"Demasiadas tentativas. O Desbloqueio facial foi desativado."</string>
<string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"Demasiadas tentativas. Em alternativa, introduza o bloqueio de ecrã."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Não é possível validar o rosto. Tente novamente."</string>
@@ -1834,7 +1834,7 @@
<string name="restr_pin_create_pin" msgid="917067613896366033">"Crie um PIN para modificar as restrições"</string>
<string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"Os PINs não correspondem. Tente novamente."</string>
<string name="restr_pin_error_too_short" msgid="1547007808237941065">"O PIN é demasiado pequeno. Deve ter, no mínimo, 4 dígitos."</string>
- <string name="restr_pin_try_later" msgid="5897719962541636727">"Tente novamente mais tarde"</string>
+ <string name="restr_pin_try_later" msgid="5897719962541636727">"Tente mais tarde"</string>
<string name="immersive_cling_title" msgid="2307034298721541791">"Visualização de ecrã inteiro"</string>
<string name="immersive_cling_description" msgid="7092737175345204832">"Para sair, deslize rapidamente para baixo a partir da parte superior."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
@@ -1959,7 +1959,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Não é possível aceder a esta app no seu dispositivo <xliff:g id="DEVICE">%1$s</xliff:g>. Em alternativa, experimente no dispositivo Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Não é possível aceder a esta app no seu dispositivo <xliff:g id="DEVICE">%1$s</xliff:g>. Em alternativa, experimente no tablet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Não é possível aceder a esta app no seu dispositivo <xliff:g id="DEVICE">%1$s</xliff:g>. Em alternativa, experimente no telemóvel."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Esta app foi concebida para uma versão mais antiga do Android e pode não funcionar corretamente. Experimente verificar se existem atualizações ou contacte o programador."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Verificar atualizações"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Tem mensagens novas"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Abra a app de SMS para ver"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a4f7da23d6a9..8635d769d9e6 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -291,7 +291,7 @@
<string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="8974401416068943888">"Modo de segurança"</string>
<string name="android_system_label" msgid="5974767339591067210">"Sistema Android"</string>
- <string name="user_owner_label" msgid="8628726904184471211">"Deslize até o perfil pessoal"</string>
+ <string name="user_owner_label" msgid="8628726904184471211">"Mudar para o perfil pessoal"</string>
<string name="managed_profile_label" msgid="7316778766973512382">"Perfil de trabalho"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"Contatos"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"acesse seus contatos"</string>
@@ -746,7 +746,7 @@
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"Permite que o proprietário use serviços da operadora. Não deve ser necessário para apps comuns."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"acessar \"Não perturbe\""</string>
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permitir que o app leia e grave a configuração \"Não perturbe\"."</string>
- <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso da permissão para visualização"</string>
+ <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"começar a usar a permissão para ver"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o sistema inicie o uso de permissão para um app. Não deve ser necessário para apps comuns."</string>
<string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"decisões de permissão da visualização inicial"</string>
<string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Autoriza o detentor a iniciar a tela para revisar as decisões de permissão. Não deve ser necessário para apps normais."</string>
@@ -1959,7 +1959,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Não é possível acessar essa configuração pelo seu <xliff:g id="DEVICE">%1$s</xliff:g>. Tente pelo dispositivo Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Não é possível acessar essa configuração pelo seu <xliff:g id="DEVICE">%1$s</xliff:g>. Tente pelo tablet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Não é possível acessar essa configuração pelo seu <xliff:g id="DEVICE">%1$s</xliff:g>. Tente pelo smartphone."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Este app foi criado para uma versão mais antiga do Android e pode não funcionar corretamente. Tente verificar se há atualizações ou entre em contato com o desenvolvedor."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Este app foi criado para uma versão mais antiga do Android. Ele pode não funcionar corretamente e não inclui as proteções de privacidade e segurança mais recentes. Verifique se há uma atualização ou entre em contato com o desenvolvedor do app."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Procurar atualizações"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Você tem mensagens novas"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Abra o app de SMS para ver"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 98f8ff2e2272..be243187f140 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1959,7 +1959,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Nu se poate accesa pe <xliff:g id="DEVICE">%1$s</xliff:g>. Încearcă pe dispozitivul Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Nu se poate accesa pe <xliff:g id="DEVICE">%1$s</xliff:g>. Încearcă pe tabletă."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Nu se poate accesa pe <xliff:g id="DEVICE">%1$s</xliff:g>. Încearcă pe telefon."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Această aplicație a fost creată pentru o versiune Android mai veche și e posibil să nu funcționeze corect. Încearcă să cauți actualizări sau contactează dezvoltatorul."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Caută actualizări"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Ai mesaje noi"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Deschide aplicația pentru SMS-uri ca să vezi"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 607da13382af..97a2efc0ba63 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1953,14 +1953,15 @@
<string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"Настройки телефона недоступны"</string>
<string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Эта функция пока недоступна на устройстве <xliff:g id="DEVICE">%1$s</xliff:g>. Используйте Android TV."</string>
<string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Эта функция пока недоступна на устройстве <xliff:g id="DEVICE">%1$s</xliff:g>. Используйте планшет."</string>
- <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Эта функция пока недоступна на устройстве <xliff:g id="DEVICE">%1$s</xliff:g>. Используйте телефон."</string>
+ <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"На устройстве <xliff:g id="DEVICE">%1$s</xliff:g> эта функция пока недоступна. Используйте телефон."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Это приложение запрашивает дополнительные меры защиты. Используйте Android TV."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Это приложение запрашивает дополнительные меры защиты. Используйте планшет."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Это приложение запрашивает дополнительные меры защиты. Используйте телефон."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Эта функция недоступна на устройстве <xliff:g id="DEVICE">%1$s</xliff:g>. Используйте Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Эта функция недоступна на устройстве <xliff:g id="DEVICE">%1$s</xliff:g>. Используйте планшет."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Эта функция недоступна на устройстве <xliff:g id="DEVICE">%1$s</xliff:g>. Используйте телефон."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Это приложение было создано для более ранней версии Android и может работать со сбоями. Проверьте наличие обновлений или свяжитесь с разработчиком."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"На устройстве <xliff:g id="DEVICE">%1$s</xliff:g> эта функция недоступна. Используйте телефон."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Проверить обновления"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Новые сообщения"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Чтобы просмотреть, откройте приложение для обмена SMS"</string>
@@ -2053,8 +2054,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Разрешить разовый доступ"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Запретить"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"В журналы записывается информация о том, что происходит на устройстве. Приложения могут использовать их, чтобы находить и устранять неполадки.\n\nТак как некоторые журналы могут содержать конфиденциальную информацию, доступ ко всем журналам следует предоставлять только тем приложениям, которым вы доверяете. \n\nЕсли вы не предоставите такой доступ этому приложению, оно по-прежнему сможет просматривать свои журналы. Не исключено, что некоторые журналы или сведения на вашем устройстве будут по-прежнему доступны его производителю."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"В журналы записывается информация о том, что происходит на устройстве. Приложения могут использовать их, чтобы находить и устранять неполадки.\n\nТак как некоторые журналы могут содержать конфиденциальную информацию, доступ ко всем журналам следует предоставлять только тем приложениям, которым вы доверяете. \n\nЕсли вы не предоставите такой доступ этому приложению, оно по-прежнему сможет просматривать свои журналы. Также некоторые журналы или сведения на вашем устройстве могут быть доступны его производителю.\n\nПодробнее: g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Больше не показывать"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Приложение \"<xliff:g id="APP_0">%1$s</xliff:g>\" запрашивает разрешение на показ фрагментов приложения \"<xliff:g id="APP_2">%2$s</xliff:g>\"."</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Изменить"</string>
@@ -2293,7 +2293,7 @@
<string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"\"<xliff:g id="APP">%1$s</xliff:g>\" работает в фоновом режиме. Нажмите, чтобы изменить настройки, связанные с расходом заряда батареи."</string>
<string name="notification_content_long_running_fgs" msgid="8258193410039977101">"Приложение \"<xliff:g id="APP">%1$s</xliff:g>\" может влиять на время работы батареи. Нажмите, чтобы увидеть активные приложения."</string>
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Проверить активные приложения"</string>
- <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"У устройства \"<xliff:g id="DEVICE">%1$s</xliff:g>\" нет доступа к камере телефона."</string>
+ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"У устройства <xliff:g id="DEVICE">%1$s</xliff:g> нет доступа к камере телефона."</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"У устройства \"<xliff:g id="DEVICE">%1$s</xliff:g>\" нет доступа к камере планшета."</string>
<string name="vdm_secure_window" msgid="161700398158812314">"Этот контент недоступен во время трансляции. Используйте телефон."</string>
<string name="system_locale_title" msgid="711882686834677268">"Системные настройки по умолчанию"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 22f13119c223..926e85ac57a4 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"මෙයට ඔබේ <xliff:g id="DEVICE">%1$s</xliff:g> මත ප්‍රවේශ විය නොහැක. ඒ වෙනුවට ඔබේ Android TV උපාංගයෙහි උත්සාහ කරන්න."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"මෙයට ඔබේ <xliff:g id="DEVICE">%1$s</xliff:g> මත ප්‍රවේශ විය නොහැක. ඒ වෙනුවට ඔබේ ටැබ්ලටයෙහි උත්සාහ කරන්න."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"මෙයට ඔබේ <xliff:g id="DEVICE">%1$s</xliff:g> මත ප්‍රවේශ විය නොහැක. ඒ වෙනුවට ඔබේ දුරකථනයෙහි උත්සාහ කරන්න."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"මෙම යෙදුම Android හි පැරණි අනුවාදයක් සඳහා තනා ඇති අතර නිසියාකාරව ක්‍රියා නොකරනු ඇත. යාවත්කාලීන සඳහා පරික්ෂා කිරීම උත්සාහ කරන්න, නැතහොත් සංවර්ධක අමතන්න."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"යාවත්කාලීන සඳහා පරික්ෂා කරන්න"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"ඔබට නව පණිවිඩ තිබේ"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"බැලීමට විවෘත SMS යෙදුම විවෘත කරන්න"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"එක් වරක් ප්‍රවේශය ඉඩ දෙන්න"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"ඉඩ නොදෙන්න"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"උපාංග ලොග ඔබේ උපාංගයෙහි සිදු වන දේ වාර්තා කරයි. ගැටලු සොයා ගැනීමට සහ නිරාකරණයට යෙදුම්වලට මෙම ලොග භාවිතා කළ හැක.\n\nසමහර ලොගවල සංවේදී තතු අඩංගු විය හැකි බැවින්, ඔබ විශ්වාස කරන යෙදුම්වලට පමණක් සියලු උපාංග ලොග වෙත ප්‍රවේශ වීමට ඉඩ දෙන්න. \n\nඔබ මෙම යෙදුමට සියලු උපාංග ලොග වෙත ප්‍රවේශ වීමට ඉඩ නොදෙන්නේ නම්, එයට තවමත් එහිම ලොග වෙත ප්‍රවේශ විය හැක. ඔබේ උපාංග නිෂ්පාදකයාට තවමත් ඔබේ උපාංගයෙහි සමහර ලොග හෝ තතු වෙත ප්‍රවේශ විය හැක."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"උපාංග ලොග ඔබේ උපාංගයෙහි සිදු වන දේ වාර්තා කරයි. ගැටලු සොයා ගැනීමට සහ නිරාකරණයට යෙදුම්වලට මෙම ලොග භාවිතා කළ හැක.\n\nසමහර ලොගවල සංවේදී තතු අඩංගු විය හැකි බැවින්, ඔබ විශ්වාස කරන යෙදුම්වලට පමණක් සියලු උපාංග ලොග වෙත ප්‍රවේශ වීමට ඉඩ දෙන්න. \n\nඔබ මෙම යෙදුමට සියලු උපාංග ලොග වෙත ප්‍රවේශ වීමට ඉඩ නොදෙන්නේ නම්, එයට තවමත් එහිම ලොග වෙත ප්‍රවේශ විය හැක. ඔබේ උපාංග නිෂ්පාදකයාට තවමත් ඔබේ උපාංගයෙහි සමහර ලොග හෝ තතු වෙත ප්‍රවේශ විය හැක.\n\ng.co/android/devicelogs හි දී තව දැන ගන්න."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"නැවත නොපෙන්වන්න"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> හට කොටස් <xliff:g id="APP_2">%2$s</xliff:g>ක් පෙන්වීමට අවශ්‍යයි"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"සංස්කරණය"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index fbe5fd2e1bb6..47e3d3bc072a 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1953,14 +1953,14 @@
<string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"Nastavenia telefónu nie sú k dispozícii"</string>
<string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> momentálne nemáte k tomuto obsahu prístup. Skúste namiesto toho použiť zariadenie Android TV."</string>
<string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> momentálne nemáte k tomuto obsahu prístup. Skúste namiesto toho použiť tablet."</string>
- <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> momentálne nemáte k tomuto obsahu prístup. Skúste namiesto toho použiť telefón."</string>
+ <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> momentálne nemáte k tomuto obsahu prístup. Skúste použiť telefón."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Táto aplikácia požaduje dodatočné zabezpečenie. Skúste namiesto toho použiť zariadenie Android TV."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Táto aplikácia požaduje dodatočné zabezpečenie. Skúste namiesto toho použiť tablet."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Táto aplikácia požaduje dodatočné zabezpečenie. Skúste namiesto toho použiť telefón."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Táto aplikácia požaduje dodatočné zabezpečenie. Skúste použiť telefón."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> momentálne nemáte prístup k tomuto obsahu. Skúste namiesto toho použiť zariadenie Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> momentálne nemáte prístup k tomuto obsahu. Skúste namiesto toho použiť tablet."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> momentálne nemáte prístup k tomuto obsahu. Skúste namiesto toho použiť telefón."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Táto aplikácia bola zostavená pre staršiu verziu Androidu a nemusí správne fungovať. Skúste skontrolovať dostupnosť aktualizácií alebo kontaktovať vývojára."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> momentálne nemáte prístup k tomuto obsahu. Skúste použiť telefón."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Táto aplikácia bola vytvorená pre staršiu verziu Androidu. Nemusí správne fungovať a obsahovať najnovšie prvky zabezpečenia a ochrany súkromia. Skontrolujte dostupnosť aktualizácie alebo kontaktujte jej vývojára."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Skontrolovať dostupnosť aktualizácie"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Máte nové správy."</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Otvorte aplikáciu pre SMS a zobrazte správu"</string>
@@ -2294,7 +2294,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Skontrolovať aktívne aplikácie"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> nemáte prístup ku kamere telefónu"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> nemáte prístup ku kamere tabletu"</string>
- <string name="vdm_secure_window" msgid="161700398158812314">"K tomuto obsahu nie je počas streamovania prístup. Skúste namiesto toho použiť telefón."</string>
+ <string name="vdm_secure_window" msgid="161700398158812314">"K tomuto obsahu nie je počas streamovania prístup. Skúste použiť telefón."</string>
<string name="system_locale_title" msgid="711882686834677268">"Predvolené systémom"</string>
<string name="default_card_name" msgid="9198284935962911468">"KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 105e7839d7df..6e722e3eee50 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1960,7 +1960,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"V napravi <xliff:g id="DEVICE">%1$s</xliff:g> ni mogoče dostopati do te vsebine. Poskusite z napravo Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"V napravi <xliff:g id="DEVICE">%1$s</xliff:g> ni mogoče dostopati do te vsebine. Poskusite s tabličnim računalnikom."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"V napravi <xliff:g id="DEVICE">%1$s</xliff:g> ni mogoče dostopati do te vsebine. Poskusite s telefonom."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ta aplikacija je bila zasnovana za starejšo različico Androida in morda ne bo delovala pravilno. Preverite, ali so na voljo posodobitve, ali pa se obrnite na razvijalca."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ta aplikacija je bila razvita za starejšo različico Androida. Morda ne bo delovala pravilno ter ne vključuje najnovejših varnostnih funkcij in funkcij za varovanje zasebnosti. Preverite, ali je na voljo posodobitev, ali pa se obrnite na razvijalca aplikacije."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Preveri, ali je na voljo posodobitev"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nova sporočila."</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Za ogled odprite aplikacijo za SMS-je"</string>
@@ -2053,8 +2053,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Dovoli enkratni dostop"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ne dovoli"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"V dnevnikih naprave se beleži dogajanje v napravi. Aplikacije lahko te dnevnike uporabijo za iskanje in odpravljanje težav.\n\nNekateri dnevniki morda vsebujejo občutljive podatke, zato dostop do vseh dnevnikov naprave omogočite le aplikacijam, ki jim zaupate. \n\nČe tej aplikaciji ne dovolite dostopa do vseh dnevnikov naprave, bo aplikacija kljub temu lahko dostopala do svojih dnevnikov. Proizvajalec naprave bo morda lahko kljub temu dostopal do nekaterih dnevnikov ali podatkov v napravi."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"V dnevnikih naprave se beleži dogajanje v napravi. Aplikacije lahko te dnevnike uporabijo za iskanje in odpravljanje težav.\n\nNekateri dnevniki morda vsebujejo občutljive podatke, zato dostop do vseh dnevnikov naprave omogočite le aplikacijam, ki jim zaupate. \n\nČe tej aplikaciji ne dovolite dostopa do vseh dnevnikov naprave, bo aplikacija kljub temu lahko dostopala do svojih dnevnikov. Proizvajalec naprave bo morda lahko kljub temu dostopal do nekaterih dnevnikov ali podatkov v napravi.\n\nPreberite več o tem na g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne prikaži več"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Aplikacija <xliff:g id="APP_0">%1$s</xliff:g> želi prikazati izreze aplikacije <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Uredi"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 3a5a5ab0343b..b1e73b57503f 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Qasja është e pamundur në <xliff:g id="DEVICE">%1$s</xliff:g>. Provoje në pajisjen Android TV më mirë."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Qasja është e pamundur në <xliff:g id="DEVICE">%1$s</xliff:g>. Provoje në tablet më mirë."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Qasja është e pamundur në <xliff:g id="DEVICE">%1$s</xliff:g>. Provoje në telefon më mirë."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ky aplikacion është ndërtuar për një version më të vjetër të Android dhe mund të mos funksionojë mirë. Provo të kontrollosh për përditësime ose kontakto me zhvilluesin."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Kontrollo për përditësim"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Ke mesazhe të reja"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Hap aplikacionin SMS për ta parë"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Lejo qasjen vetëm për një herë"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Mos lejo"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Evidencat e pajisjes regjistrojnë çfarë ndodh në pajisjen tënde. Aplikacionet mund t\'i përdorin këto evidenca për të gjetur dhe rregulluar problemet.\n\nDisa evidenca mund të përmbajnë informacione delikate, ndaj lejo vetëm aplikacionet që u beson të kenë qasje te të gjitha evidencat e pajisjes. \n\nNëse nuk e lejon këtë aplikacion që të ketë qasje te të gjitha evidencat e pajisjes, ai mund të vazhdojë të ketë qasje tek evidencat e tij. Prodhuesi i pajisjes sate mund të jetë ende në gjendje që të ketë qasje te disa evidenca ose informacione në pajisjen tënde."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Evidencat e pajisjes regjistrojnë çfarë ndodh në pajisjen tënde. Aplikacionet mund t\'i përdorin këto evidenca për të gjetur dhe rregulluar problemet.\n\nDisa evidenca mund të përmbajnë informacione delikate, ndaj lejo vetëm aplikacionet që u beson të kenë qasje te të gjitha evidencat e pajisjes. \n\nNëse nuk e lejon këtë aplikacion që të ketë qasje te të gjitha evidencat e pajisjes, ai mund të vazhdojë të ketë qasje tek evidencat e tij. Prodhuesi i pajisjes sate mund të jetë ende në gjendje që të ketë qasje te disa evidenca ose informacione në pajisjen tënde.\n\nMëso më shumë në g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Mos e shfaq më"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> dëshiron të shfaqë pjesë të <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Modifiko"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index ff87cdba1db2..0e50810da512 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1959,7 +1959,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Овој апликацији не може да се приступи са уређаја <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на Android TV уређају."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Овој апликацији не може да се приступи са уређаја <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на таблету."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Овој апликацији не може да се приступи са уређаја <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на телефону."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ова апликација је направљена за старију верзију Android-а, па можда неће радити исправно. Потражите ажурирања или контактирајте програмера."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ова апликација је направљена за старију верзију Android-а. Можда неће радити исправно и не обухвата најновије безбедносне функције и заштите приватности. Проверите да ли има ажурирања или се обратите програмеру апликације."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Потражи ажурирање"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Имате нове поруке"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Отворите апликацију за SMS да бисте прегледали"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 34f0f4f3ba75..8a3f98679ff7 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Det går inte att streama detta till <xliff:g id="DEVICE">%1$s</xliff:g>. Testa med Android TV-enheten i stället."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Det går inte att streama detta till <xliff:g id="DEVICE">%1$s</xliff:g>. Testa med surfplattan i stället."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Det går inte att streama detta till <xliff:g id="DEVICE">%1$s</xliff:g>. Testa med telefonen i stället."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Appen har utvecklats för en äldre version av Android och kanske inte fungerar som den ska. Testa att söka efter uppdateringar eller kontakta utvecklaren."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Sök efter uppdateringar"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Du har nya meddelanden"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Öppna sms-appen och visa meddelandet"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Tillåt engångsåtkomst"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Tillåt inte"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"I enhetsloggar registreras vad som händer på enheten. Appar kan använda dessa loggar för att hitta och åtgärda problem.\n\nVissa loggar kan innehålla känsliga uppgifter, så du ska bara bevilja appar du litar på åtkomst till alla enhetsloggar. \n\nEn app har åtkomst till sina egna loggar även om du inte ger den åtkomst till alla enhetsloggar. Enhetens tillverkare kan fortfarande ha åtkomst till vissa loggar eller viss information på enheten."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Enhetsloggar registrerar vad som händer på enheten. Appar kan använda dessa loggar för att hitta och åtgärda problem.\n\nVissa loggar kan innehålla känsliga uppgifter, så du ska bara bevilja appar du litar på åtkomst till alla enhetsloggar. \n\nEn app har åtkomst till sina egna loggar även om du inte ger den åtkomst till alla enhetsloggar. Enhetens tillverkare kan fortfarande ha åtkomst till vissa loggar eller viss information på enheten.\n\nLäs mer på g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Visa inte igen"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vill kunna visa bitar av <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Redigera"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 9c8aecd3c71e..5829fae5dbcf 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Huwezi kufikia mipangilio hii kwenye <xliff:g id="DEVICE">%1$s</xliff:g> yako. Badala yake jaribu kwenye kifaa chako cha Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Huwezi kufikia mipangilio hii kwenye <xliff:g id="DEVICE">%1$s</xliff:g> yako. Badala yake jaribu kwenye kompyuta yako kibao."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Huwezi kufikia mipangilio hii kwenye <xliff:g id="DEVICE">%1$s</xliff:g> yako. Badala yake jaribu kwenye simu yako."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Programu hii iliundwa kwa ajili ya toleo la zamani la Android na huenda isifanye kazi vizuri. Jaribu kuangalia masasisho au uwasiliane na msanidi programu."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Programu hii ilisanidiwa kwa ajili ya toleo la zamani la Android. Huenda isifanye kazi ipasavyo na haijumuishi ulinzi wa faragha na usalama wa hivi karibuni. Angalia kama ina sasisho au wasiliana na msanidi wa programu."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Angalia masasisho"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Una ujumbe mpya"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Fungua programu ya SMS ili uweze kuangalia"</string>
@@ -2051,8 +2051,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Ruhusu ufikiaji wa mara moja"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Usiruhusu"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Kumbukumbu za kifaa zinarekodi kinachofanyika kwenye kifaa chako. Programu zinaweza kutumia kumbukumbu hizi ili kutambua na kurekebisha hitilafu.\n\nBaadhi ya kumbukumbu huenda zikawa na taarifa nyeti, hivyo ruhusu tu programu unazoziamini kufikia kumbukumbu zote za kifaa. \n\nIwapo hutaruhusu programu hii ifikie kumbukumbu zote za kifaa, bado inaweza kufikia kumbukumbu zake yenyewe. Huenda mtengenezaji wa kifaa chako bado akaweza kufikia baadhi ya kumbukumbu au taarifa zilizopo kwenye kifaa chako."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Kumbukumbu za kifaa hurekodi kinachofanyika kwenye kifaa chako. Programu zinaweza kutumia kumbukumbu hizi ili kutambua na kurekebisha hitilafu.\n\nBaadhi ya kumbukumbu huenda zikawa na taarifa nyeti, hivyo ruhusu tu programu unazoziamini kufikia kumbukumbu zote za kifaa. \n\nIwapo hutaruhusu programu hii ifikie kumbukumbu zote za kifaa, bado inaweza kufikia kumbukumbu zake yenyewe. Huenda mtengenezaji wa kifaa chako bado akaweza kufikia baadhi ya kumbukumbu au taarifa zilizopo kwenye kifaa chako.\n\nPata maelezo zaidi kwenye g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Usionyeshe tena"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> inataka kuonyesha vipengee <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Badilisha"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 699495fb7faf..8d0103ea6c52 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"உங்கள் <xliff:g id="DEVICE">%1$s</xliff:g> சாதனத்தில் இதை அணுக முடியாது. அதற்குப் பதிலாக உங்கள் Android TV சாதனத்தில் முயன்று பாருங்கள்."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"உங்கள் <xliff:g id="DEVICE">%1$s</xliff:g> சாதனத்தில் இதை அணுக முடியாது. அதற்குப் பதிலாக உங்கள் டேப்லெட்டில் முயன்று பாருங்கள்."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"உங்கள் <xliff:g id="DEVICE">%1$s</xliff:g> சாதனத்தில் இதை அணுக முடியாது. அதற்குப் பதிலாக உங்கள் மொபைலில் முயன்று பாருங்கள்."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"இந்த ஆப்ஸ் Android இன் பழைய பதிப்புக்காக உருவாக்கப்பட்டதால், சரியாக வேலை செய்யாமல் போகலாம். புதுப்பிப்புகள் ஏதேனும் உள்ளதா எனப் பார்க்கவும் அல்லது டெவெலப்பரைத் தொடர்புகொள்ளவும்."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"புதுப்பிப்பு உள்ளதா எனப் பார்"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"புதிய செய்திகள் வந்துள்ளன"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"பார்க்க, SMS பயன்பாட்டைத் திறக்கவும்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index a771e607f542..c88475b754b4 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -494,7 +494,7 @@
<string name="permlab_setWallpaper" msgid="6959514622698794511">"వాల్‌పేపర్‌ను సెట్ చేయడం"</string>
<string name="permdesc_setWallpaper" msgid="2973996714129021397">"సిస్టమ్ వాల్‌పేపర్‌ను సెట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_setWallpaperHints" msgid="1153485176642032714">"మీ వాల్‌పేపర్ పరిమాణాన్ని సర్దుబాటు చేయడం"</string>
- <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"సిస్టమ్ వాల్‌పేపర్ పరిమాణం సూచనలను సెట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+ <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"సిస్టమ్ వాల్‌పేపర్ సైజ్‌ సూచనలను సెట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_setTimeZone" msgid="7922618798611542432">"సమయ మండలిని సెట్ చేయడం"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"టాబ్లెట్ యొక్క సమయ మండలిని మార్చడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"మీ Android TV పరికరం సమయ మండలిని మార్చడానికి యాప్‌ని అనుమతిస్తుంది."</string>
@@ -1444,7 +1444,7 @@
<string name="ext_media_status_ejecting" msgid="7532403368044013797">"తొలగిస్తోంది…"</string>
<string name="ext_media_status_formatting" msgid="774148701503179906">"ఫార్మాట్ చేస్తోంది..."</string>
<string name="ext_media_status_missing" msgid="6520746443048867314">"చొప్పించబడలేదు"</string>
- <string name="activity_list_empty" msgid="4219430010716034252">"సరిపోలే కార్యాచరణలు కనుగొనబడలేదు."</string>
+ <string name="activity_list_empty" msgid="4219430010716034252">"మ్యాచ్ అయ్యే కార్యాచరణలు కనుగొనబడలేదు."</string>
<string name="permlab_route_media_output" msgid="8048124531439513118">"మీడియా అవుట్‌పుట్‌ను మళ్లించడం"</string>
<string name="permdesc_route_media_output" msgid="1759683269387729675">"మీడియా అవుట్‌పుట్‌ను ఇతర బాహ్య పరికరాలకు మళ్లించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_readInstallSessions" msgid="7279049337895583621">"ఇన్‌స్టాల్ సెషన్‌లను చదవడం"</string>
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"మీ <xliff:g id="DEVICE">%1$s</xliff:g>లో దీన్ని యాక్సెస్ చేయడం సాధ్యపడదు. బదులుగా మీ Android TV పరికరంలో ట్రై చేయండి."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"మీ <xliff:g id="DEVICE">%1$s</xliff:g>లో దీన్ని యాక్సెస్ చేయడం సాధ్యపడదు. బదులుగా మీ టాబ్లెట్‌లో ట్రై చేయండి."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"మీ <xliff:g id="DEVICE">%1$s</xliff:g>లో దీన్ని యాక్సెస్ చేయడం సాధ్యపడదు. బదులుగా మీ ఫోన్‌లో ట్రై చేయండి."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ఈ యాప్ పాత వెర్షన్ Android కోసం రూపొందించబడింది మరియు అది సరిగ్గా పని చేయకపోవచ్చు. అప్‌డేట్‌ల కోసం చెక్ చేయడానికి ప్రయత్నించండి లేదా డెవలపర్‌ని సంప్రదించండి."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"ఈ యాప్ పాత Android వెర్షన్ కోసం రూపొందించబడింది. ఇది సరిగ్గా పని చేయకపోవచ్చు, ఇంకా దీనిలో తాజా సెక్యూరిటీ, గోప్యతా రక్షణలు ఉండకపోవచ్చు. అప్‌డేట్ కోసం చెక్ చేయండి, లేదా యాప్ డెవలపర్‌ను సంప్రదించండి."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"అప్‌డేట్ కోసం చెక్ చేయండి"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"మీకు కొత్త మెసేజ్‌లు ఉన్నాయి"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"చూడటానికి SMS యాప్‌ను తెరవండి"</string>
@@ -2051,8 +2051,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"వన్-టైమ్ యాక్సెస్‌ను అనుమతించండి"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"అనుమతించవద్దు"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"మీ పరికరంలో జరిగే దాన్ని పరికర లాగ్‌లు రికార్డ్ చేస్తాయి. సమస్యలను కనుగొని, పరిష్కరించడానికి యాప్‌లు ఈ లాగ్‌లను ఉపయోగిస్తాయి.\n\nకొన్ని లాగ్‌లలో గోప్యమైన సమాచారం ఉండవచ్చు, కాబట్టి మీరు విశ్వసించే యాప్‌లను మాత్రమే అన్ని పరికర లాగ్‌లను యాక్సెస్ చేయడానికి అనుమతించండి. \n\nఅన్ని పరికర లాగ్‌లను యాక్సెస్ చేయడానికి మీరు ఈ యాప్‌ను అనుమతించకపోతే, అది తన స్వంత లాగ్‌లను ఇప్పటికి యాక్సెస్ చేయగలదు. మీ పరికర తయారీదారు ఇప్పటికీ మీ పరికరంలో కొన్ని లాగ్‌లు లేదా సమాచారాన్ని యాక్సెస్ చేయగలరు."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"మీ పరికరంలో జరిగే దాన్ని పరికర లాగ్‌లు రికార్డ్ చేస్తాయి. సమస్యలను కనుగొని, పరిష్కరించడానికి యాప్‌లు ఈ లాగ్‌లను ఉపయోగిస్తాయి.\n\nకొన్ని లాగ్‌లలో గోప్యమైన సమాచారం ఉండవచ్చు, కాబట్టి మీరు విశ్వసించే యాప్‌లను మాత్రమే అన్ని పరికర లాగ్‌లను యాక్సెస్ చేయడానికి అనుమతించండి. \n\nఅన్ని పరికర లాగ్‌లను యాక్సెస్ చేయడానికి మీరు ఈ యాప్‌ను అనుమతించకపోతే, అది తన స్వంత లాగ్‌లను ఇప్పటికి యాక్సెస్ చేయగలదు. మీ పరికర తయారీదారు ఇప్పటికీ మీ పరికరంలో కొన్ని లాగ్‌లు లేదా సమాచారాన్ని యాక్సెస్ చేయగలరు.\n\ng.co/android/devicelogsలో దీని గురించి మరింత తెలుసుకోండి."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"మళ్లీ చూపవద్దు"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g> స్లైస్‌లను చూపించాలనుకుంటోంది"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"ఎడిట్ చేయండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 44a810bcdc47..447b42b06b61 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"เข้าถึงการตั้งค่านี้ใน <xliff:g id="DEVICE">%1$s</xliff:g> ของคุณไม่ได้ โปรดลองเข้าถึงในอุปกรณ์ Android TV แทน"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"เข้าถึงการตั้งค่านี้ใน <xliff:g id="DEVICE">%1$s</xliff:g> ของคุณไม่ได้ โปรดลองเข้าถึงในแท็บเล็ตแทน"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"เข้าถึงการตั้งค่านี้ใน <xliff:g id="DEVICE">%1$s</xliff:g> ของคุณไม่ได้ โปรดลองเข้าถึงในโทรศัพท์แทน"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"แอปนี้สร้างขึ้นเพื่อ Android เวอร์ชันเก่าและอาจทำงานผิดปกติ โปรดลองตรวจหาการอัปเดตหรือติดต่อนักพัฒนาซอฟต์แวร์"</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"แอปนี้สร้างมาเพื่อ Android เวอร์ชันเก่า ซึ่งอาจทำงานไม่ถูกต้องและไม่มีการคุ้มครองความปลอดภัยและความเป็นส่วนตัวเวอร์ชันล่าสุด ตรวจหาอัปเดตหรือติดต่อนักพัฒนาแอป"</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ตรวจสอบอัปเดต"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"คุณมีข้อความใหม่"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"เปิดแอป SMS เพื่อดู"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 156ba692d4e3..0947f25ffa0f 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Hindi ito maa-access sa iyong <xliff:g id="DEVICE">%1$s</xliff:g>. Subukan na lang sa iyong Android TV device."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Hindi ito maa-access sa iyong <xliff:g id="DEVICE">%1$s</xliff:g>. Subukan na lang sa iyong tablet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Hindi ito maa-access sa iyong <xliff:g id="DEVICE">%1$s</xliff:g>. Subukan na lang sa iyong telepono."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ang app na ito ay ginawa para sa mas lumang bersyon ng Android at maaaring hindi gumana nang maayos. Subukang tingnan kung may mga update, o makipag-ugnayan sa developer."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ginawa ang app na ito para sa mas lumang bersyon ng Android. Baka hindi ito gumana nang maayos at wala itong pinakabagong proteksyon sa seguridad at privacy. Tingnan kung may update, o makipag-ugnayan sa developer ng app."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Tingnan kung may update"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Mayroon kang mga bagong mensahe"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Buksan ang SMS app upang tingnan"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 9e782f28221d..d4a30e9f928a 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Bu uygulamaya <xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan erişilemiyor. Bunun yerine Android TV cihazınızı kullanmayı deneyin."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Bu uygulamaya <xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan erişilemiyor. Bunun yerine tabletinizi kullanmayı deneyin."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Bu uygulamaya <xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan erişilemiyor. Bunun yerine telefonunuzu kullanmayı deneyin."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Bu uygulama Android\'in daha eski bir sürümü için oluşturuldu ve düzgün çalışmayabilir. Güncellemeleri kontrol etmeyi deneyin veya geliştiriciyle iletişime geçin."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Güncellemeleri denetle"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Yeni mesajlarınız var"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Görüntülemek için SMS uygulamasını açın"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Tek seferlik erişim izni ver"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"İzin verme"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Cihaz günlükleri, cihazınızda olanları kaydeder. Uygulamalar, sorunları bulup düzeltmek için bu günlükleri kullanabilir.\n\nBazı günlükler hassas bilgiler içerebileceği için yalnızca güvendiğiniz uygulamaların tüm cihaz günlüklerine erişmesine izin verin. \n\nBu uygulamanın tüm cihaz günlüklerine erişmesine izin vermeseniz de kendi günlüklerine erişmeye devam edebilir. Ayrıca, cihaz üreticiniz de cihazınızdaki bazı günlüklere veya bilgilere erişmeye devam edebilir."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Cihaz günlükleri, cihazınızda olanları kaydeder. Uygulamalar, sorunları bulup düzeltmek için bu günlükleri kullanabilir.\n\nBazı günlükler hassas bilgiler içerebileceği için yalnızca güvendiğiniz uygulamaların tüm cihaz günlüklerine erişmesine izin verin. \n\nBu uygulamanın tüm cihaz günlüklerine erişmesine izin vermeseniz de kendi günlüklerine erişmeye devam edebilir. Ayrıca, cihaz üreticiniz de cihazınızdaki bazı günlüklere veya bilgilere erişmeye devam edebilir.\n\nDaha fazla bilgiyi g.co/android/devicelogs sayfasında bulabilirsiniz."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Bir daha gösterme"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> uygulaması, <xliff:g id="APP_2">%2$s</xliff:g> dilimlerini göstermek istiyor"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Düzenle"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index e63e2acaf387..522ff6d382af 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1960,7 +1960,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Немає доступу на вашому пристрої (<xliff:g id="DEVICE">%1$s</xliff:g>). Спробуйте натомість скористатися пристроєм Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Немає доступу на вашому пристрої (<xliff:g id="DEVICE">%1$s</xliff:g>). Спробуйте натомість скористатися планшетом."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Немає доступу на вашому пристрої (<xliff:g id="DEVICE">%1$s</xliff:g>). Спробуйте натомість скористатися телефоном."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Цей додаток створений для старішої версії Android і може працювати неналежним чином. Спробуйте знайти оновлення або зв’яжіться з розробником."</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Шукати оновлення"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"У вас є нові повідомлення"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Щоб переглянути, відкрийте додаток для SMS"</string>
@@ -2053,8 +2054,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Надати доступ лише цього разу"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Не дозволяти"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"У журналах пристрою реєструється все, що відбувається на ньому. За допомогою цих журналів додатки можуть виявляти й усувати проблеми.\n\nДеякі журнали можуть містити конфіденційні дані, тому надавати доступ до всіх журналів пристрою слід лише надійним додаткам. \n\nЯкщо додаток не має доступу до всіх журналів пристрою, він усе одно може використовувати власні журнали. Виробник вашого пристрою все одно може використовувати деякі журнали чи інформацію на ньому."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"У журналах пристрою реєструється все, що відбувається на ньому. За допомогою цих журналів додатки можуть виявляти й усувати проблеми.\n\nДеякі журнали можуть містити конфіденційні дані, тому надавати доступ до всіх журналів пристрою слід лише надійним додаткам. \n\nЯкщо додаток не має доступу до всіх журналів пристрою, він усе одно може використовувати власні журнали. Виробник вашого пристрою все одно може використовувати деякі журнали чи інформацію на ньому.\n\nДокладніше: g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Більше не показувати"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> хоче показати фрагменти додатка <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Редагувати"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 21a0818884fd..39ee0bf28d9f 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"‏آپ کے <xliff:g id="DEVICE">%1$s</xliff:g> پر اس تک رسائی حاصل نہیں ہو سکتی۔ اس کے بجائے اپنے Android TV آلے پر کوشش کریں۔"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"آپ کے <xliff:g id="DEVICE">%1$s</xliff:g> پر اس تک رسائی حاصل نہیں ہو سکتی۔ اس کے بجائے اپنے ٹیبلیٹ پر کوشش کریں۔"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"آپ کے <xliff:g id="DEVICE">%1$s</xliff:g> پر اس تک رسائی حاصل نہیں ہو سکتی۔ اس کے بجائے اپنے فون پر کوشش کریں۔"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏یہ ایپ Android کے پرانے ورژن کے لئے بنائی گئی ہے اور ہو سکتا ہے صحیح طور پر کام نہ کرے۔ اپ ڈیٹس چیک کر کے آزمائیں یا ڈویلپر سے رابطہ کریں۔"</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"اپ ڈیٹ چیک کریں"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"آپ کے پاس نئے پیغامات ہیں"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"‏دیکھنے کیلئے SMS ایپ کھولیں"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"یک وقتی رسائی کی اجازت دیں"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"اجازت نہ دیں"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"آپ کے آلے پر جو ہوتا ہے آلے کے لاگز اسے ریکارڈ کر لیتے ہیں۔ ایپس ان لاگز کا استعمال مسائل کو تلاش کرنے اور ان کو حل کرنے کے لیے کر سکتی ہیں۔\n\nکچھ لاگز میں حساس معلومات شامل ہو سکتی ہیں، اس لیے صرف اپنے بھروسے مند ایپس کو ہی آلے کے تمام لاگز تک رسائی کی اجازت دیں۔ \n\nاگر آپ اس ایپ کو آلے کے تمام لاگز تک رسائی کی اجازت نہیں دیتے ہیں تب بھی یہ اپنے لاگز تک رسائی حاصل کر سکتی ہے۔ آپ کے آلے کا مینوفیکچرر اب بھی آپ کے آلے پر کچھ لاگز یا معلومات تک رسائی حاصل کر سکتا ہے۔"</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"‏آپ کے آلے پر جو ہوتا ہے آلے کے لاگز اسے ریکارڈ کر لیتے ہیں۔ ایپس ان لاگز کا استعمال مسائل کو تلاش کرنے اور ان کو حل کرنے کے لیے کر سکتی ہیں۔\n\nکچھ لاگز میں حساس معلومات شامل ہو سکتی ہیں، اس لیے صرف اپنی بھروسے مند ایپس کو ہی آلے کے تمام لاگز تک رسائی کی اجازت دیں۔ \n\nاگر آپ اس ایپ کو آلے کے تمام لاگز تک رسائی کی اجازت نہیں دیتے ہیں تب بھی یہ اپنے لاگز تک رسائی حاصل کر سکتی ہے۔ آپ کے آلے کا مینوفیکچرر اب بھی آپ کے آلے پر کچھ لاگز یا معلومات تک رسائی حاصل کر سکتا ہے۔\n\ng.co/android/devicelogs پر مزید جانیں۔"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"دوبارہ نہ دکھائیں"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g> کے سلائسز دکھانا چاہتی ہے"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"ترمیم کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index b23796cb64cc..cf4478d1c47e 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Bu <xliff:g id="DEVICE">%1$s</xliff:g> qurilmangizda ochilmaydi. Android TV qurilmasi orqali urinib koʻring."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Bu <xliff:g id="DEVICE">%1$s</xliff:g> qurilmangizda ochilmaydi. Planshet orqali urinib koʻring."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Bu <xliff:g id="DEVICE">%1$s</xliff:g> qurilmangizda ochilmaydi. Telefon orqali urininb koʻring."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Bu ilova eskiroq Android versiyalariga chiqarilgan va xato ishlashi mumkin. Yangilanishlarini tekshiring yoki dasturchi bilan bog‘laning."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Bu ilova Androidning eskiroq versiyasiga moʻljallab ishlab chiqilgan. Xatosiz ishlashi mumkin, lekin xavfsizlik va maxfiylik himoyasiga oid oxirgi yangilanishlarini olmaydi. Yangilanish borligini tekshiring yoki ilova ishlab chiquvchisiga murojaat qiling."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Yangilanish borligini tekshirish"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Sizga yangi SMS keldi"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Ko‘rish uchun SMS ilovasini oching"</string>
@@ -2051,8 +2051,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Bir matalik foydalanishga ruxsat berish"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Rad etish"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Qurilma jurnaliga qurilma bilan yuz bergan hodisalar qaydlari yoziladi. Ilovalar bu jurnal qaydlari yordamida muammolarni topishi va bartaraf qilishi mumkin.\n\nAyrim jurnal qaydlarida maxfiy axborotlar yozilishi mumkin, shu sababli qurilmadagi barcha jurnal qaydlariga ruxsatni faqat ishonchli ilovalarga bering. \n\nBu ilovaga qurilmadagi barcha jurnal qaydlariga ruxsat berilmasa ham, u oʻzining jurnalini ocha oladi. Qurilma ishlab chiqaruvchisi ham ayrim jurnallar yoki qurilma haqidagi axborotlarni ocha oladi."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Qurilma jurnaliga qurilma bilan yuz bergan hodisalar qaydlari yoziladi. Ilovalar bu jurnal qaydlari yordamida muammolarni topishi va bartaraf qilishi mumkin.\n\nAyrim jurnal qaydlarida maxfiy axborotlar yozilishi mumkin, shu sababli qurilmadagi barcha jurnal qaydlariga ruxsatni faqat ishonchli ilovalarga bering. \n\nBu ilovaga qurilmadagi barcha jurnal qaydlariga ruxsat berilmasa ham, u oʻzining jurnalini ocha oladi. Qurilma ishlab chiqaruvchisi ham ayrim jurnallar yoki qurilma haqidagi axborotlarni ocha oladi.\n\nBatafsil: g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Boshqa chiqmasin"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ilovasi <xliff:g id="APP_2">%2$s</xliff:g> ilovasidan fragmentlar ko‘rsatish uchun ruxsat so‘ramoqda"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Tahrirlash"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 2bb8a0575764..1a1bb91f7d5d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Hiện tại, bạn không thể truy cập vào ứng dụng này trên <xliff:g id="DEVICE">%1$s</xliff:g>. Hãy thử trên thiết bị Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Hiện tại, bạn không thể truy cập vào ứng dụng này trên <xliff:g id="DEVICE">%1$s</xliff:g>. Hãy thử trên máy tính bảng."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Hiện tại, bạn không thể truy cập vào ứng dụng này trên <xliff:g id="DEVICE">%1$s</xliff:g>. Hãy thử trên điện thoại."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ứng dụng này được xây dựng cho một phiên bản Android cũ hơn và có thể hoạt động không bình thường. Hãy thử kiểm tra các bản cập nhật hoặc liên hệ với nhà phát triển."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ứng dụng này được xây dựng cho một phiên bản Android cũ. Ứng dụng này có thể không hoạt động đúng cách và không có các biện pháp bảo vệ mới nhất về bảo mật và quyền riêng tư. Hãy kiểm tra để tìm bản cập nhật hoặc liên hệ với nhà phát triển của ứng dụng."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Kiểm tra để tìm bản cập nhật"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Bạn có tin nhắn mới"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Mở ứng dụng SMS để xem"</string>
@@ -2051,8 +2051,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Cho phép truy cập một lần"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Không cho phép"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Nhật ký thiết bị ghi lại những hoạt động diễn ra trên thiết bị. Các ứng dụng có thể dùng nhật ký này để tìm và khắc phục sự cố.\n\nMột số nhật ký có thể chứa thông tin nhạy cảm, vì vậy, bạn chỉ nên cấp quyền truy cập vào toàn bộ nhật ký thiết bị cho những ứng dụng mà mình tin cậy. \n\nNếu bạn không cho phép ứng dụng này truy cập vào toàn bộ nhật ký thiết bị, thì ứng dụng vẫn có thể truy cập vào nhật ký của chính nó. Nhà sản xuất thiết bị vẫn có thể truy cập vào một số nhật ký hoặc thông tin trên thiết bị của bạn."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Nhật ký thiết bị ghi lại những hoạt động diễn ra trên thiết bị. Các ứng dụng có thể dùng các nhật ký này để tìm và khắc phục sự cố.\n\nMột số nhật ký có thể chứa thông tin nhạy cảm, vì vậy, bạn chỉ nên cấp quyền truy cập vào mọi nhật ký thiết bị cho những ứng dụng mà mình tin cậy. \n\nNếu bạn không cho phép ứng dụng này truy cập vào toàn bộ nhật ký thiết bị, thì ứng dụng vẫn có thể truy cập vào nhật ký của chính nó. Nhà sản xuất thiết bị vẫn có thể truy cập vào một số nhật ký hoặc thông tin trên thiết bị của bạn.\n\nTìm hiểu thêm tại g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Không hiện lại"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> muốn hiển thị các lát của <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Chỉnh sửa"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index e558031c7cfd..75b9e0ed10b2 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1560,7 +1560,7 @@
<string name="content_description_sliding_handle" msgid="982510275422590757">"滑动手柄。触摸并按住。"</string>
<string name="description_target_unlock_tablet" msgid="7431571180065859551">"滑动解锁。"</string>
<string name="action_bar_home_description" msgid="1501655419158631974">"导航首页"</string>
- <string name="action_bar_up_description" msgid="6611579697195026932">"向上导航"</string>
+ <string name="action_bar_up_description" msgid="6611579697195026932">"返回"</string>
<string name="action_menu_overflow_description" msgid="4579536843510088170">"更多选项"</string>
<string name="action_bar_home_description_format" msgid="5087107531331621803">"%1$s:%2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s - %2$s:%3$s"</string>
@@ -1958,7 +1958,8 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"无法在您的<xliff:g id="DEVICE">%1$s</xliff:g>上访问此设置,您可以尝试在 Android TV 设备上访问。"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"无法在您的<xliff:g id="DEVICE">%1$s</xliff:g>上访问此设置,您可以尝试在平板电脑上访问。"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"无法在您的<xliff:g id="DEVICE">%1$s</xliff:g>上访问此设置,您可以尝试在手机上访问。"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"此应用专为旧版 Android 打造,因此可能无法正常运行。请尝试检查更新或与开发者联系。"</string>
+ <!-- no translation found for deprecated_target_sdk_message (5246906284426844596) -->
+ <skip />
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"检查更新"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"您有新消息"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"打开短信应用查看"</string>
@@ -2051,8 +2052,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"允许访问一次"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"不允许"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"设备日志会记录设备上发生的活动。应用可以使用这些日志查找和修复问题。\n\n部分日志可能包含敏感信息,因此请仅允许您信任的应用访问所有设备日志。\n\n如果您不授予此应用访问所有设备日志的权限,它仍然可以访问自己的日志。您的设备制造商可能仍然能够访问设备上的部分日志或信息。"</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"设备日志会记录设备上发生的活动。应用可以使用这些日志查找和修复问题。\n\n部分日志可能包含敏感信息,因此请仅允许您信任的应用访问所有设备日志。\n\n如果您不授予此应用访问所有设备日志的权限,它仍然可以访问自己的日志。您的设备制造商可能仍然能够访问设备上的部分日志或信息。\n\n如需了解详情,请访问 g.co/android/devicelogs。"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"不再显示"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"“<xliff:g id="APP_0">%1$s</xliff:g>”想要显示“<xliff:g id="APP_2">%2$s</xliff:g>”图块"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"编辑"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index f4f12c14bda4..ca667c4ac708 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取此應用程式,請改用 Android TV 裝置。"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取此應用程式,請改用平板電腦。"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取此應用程式,請改用手機。"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"此應用程式專為舊版 Android 打造,因此可能無法正常運作。請嘗試檢查更新,或與開發人員聯絡。"</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"這個應用程式是專為舊版 Android 所打造,因此可能無法正常運作,且不提供最新的安全性和隱私保護服務。請檢查是否有更新版本,或與應用程式的開發人員聯絡。"</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"檢查更新"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"您有新的訊息"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"開啟短訊應用程式查看內容"</string>
@@ -2051,8 +2051,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"允許存取一次"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"不允許"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"裝置記錄會記下裝置上的活動。應用程式可透過這些記錄找出並修正問題。\n\n部分記錄可能包含敏感資料,因此請只允許信任的應用程式存取所有裝置記錄。\n\n如果不允許此應用程式存取所有裝置記錄,此應用程式仍能存取自己的記錄,且裝置製造商可能仍可存取裝置上的部分記錄或資料。"</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"裝置記錄會記下裝置上的活動。應用程式可使用這些記錄找出並修正問題。\n\n有些記錄可能包含敏感資料,因此建議只允許信任的應用程式存取所有裝置記錄。\n\n如果不允許此應用程式存取所有裝置記錄,此應用程式仍能存取自己的記錄。您的裝置製造商可能仍可存取裝置上的一些記錄或資料。\n\n詳情請瀏覽 g.co/android/devicelogs。"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"不要再顯示"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"「<xliff:g id="APP_0">%1$s</xliff:g>」想顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的快訊"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"編輯"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index f4bfdda51a69..4baced82c846 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1950,15 +1950,15 @@
<string name="app_streaming_blocked_title_for_settings_dialog" product="tablet" msgid="8222710146267948647">"無法使用平板電腦設定"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"無法使用手機設定"</string>
<string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"目前無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取這個應用程式,請改用 Android TV 裝置。"</string>
- <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"目前無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取這個應用程式,請改用平板電腦。"</string>
+ <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"目前無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取這個項目,請改用平板電腦。"</string>
<string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"目前無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取這個應用程式,請改用手機。"</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"這個應用程式要求進行額外的安全性驗證,請改用 Android TV 裝置。"</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"這個應用程式要求進行額外的安全性驗證,請改用平板電腦。"</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"這個應用程式要求進行額外的安全性驗證,請改用手機。"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取這個應用程式,請改用 Android TV 裝置。"</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取這個應用程式,請改用平板電腦。"</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取這個項目,請改用平板電腦。"</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取這個應用程式,請改用手機。"</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"這個應用程式是專為舊版 Android 所打造,因此可能無法正常運作。請嘗試檢查更新,或是與開發人員聯絡。"</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"這個應用程式是專為舊版 Android 所打造,因此可能無法正常運作,且不提供最新的安全性和隱私保護服務。請檢查是否有更新版本,或與應用程式的開發人員聯絡。"</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"檢查更新"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"你有新訊息"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"開啟簡訊應用程式來查看內容"</string>
@@ -2051,8 +2051,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"允許一次性存取"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"不允許"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"系統會透過裝置記錄記下裝置上的活動。應用程式可以根據這些記錄找出問題並進行修正。\n\n某些記錄可能含有機密資訊,因此請勿讓不信任的應用程式存取所有裝置記錄。\n\n即使你不允許這個應用程式存取所有裝置記錄,這個應用程式仍能存取自己的記錄,而且裝置製造商或許仍可存取裝置的某些記錄或資訊。"</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"裝置記錄會記下裝置上的活動。應用程式可以根據這些記錄找出問題並進行修正。\n\n由於某些記錄可能含有機密資訊,建議只讓信任的應用程式存取所有裝置記錄。\n\n如果你不允許這個應用程式存取所有裝置記錄,這個應用程式仍可存取屬於自己的記錄,而裝置製造商也或許還是可以存取裝置的某些記錄或資訊。\n\n請參閱以下網址瞭解詳情:g.co/android/devicelogs。"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"不要再顯示"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"「<xliff:g id="APP_0">%1$s</xliff:g>」想要顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的區塊"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"編輯"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 8f50ab042ef7..23a557af6f46 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1958,7 +1958,7 @@
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Lokhu akukwazi ukufinyelelwa ku-<xliff:g id="DEVICE">%1$s</xliff:g> yakho. Zama kudivayisi yakho ye-Android TV kunalokho."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Lokhu akukwazi ukufinyelelwa ku-<xliff:g id="DEVICE">%1$s</xliff:g> yakho. Zama kuthebulethi yakho kunalokho."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Lokhu akukwazi ukufinyelelwa ku-<xliff:g id="DEVICE">%1$s</xliff:g> yakho. Zama efonini yakho kunalokho."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Lolu hlelo lokusebenza belakhelwe inguqulo endala ye-Android futhi kungenzeka lungasebenzi kahle. Zama ukuhlolela izibuyekezo, noma uxhumane nonjiniyela."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Le app yakhelwe uhlobo lwakudala le-Android. Ingase ingasebenzi kahle futhi ayinakho ukuvikeleka kwakamuva nokuvikelwa kobumfihlo. Hlola isibuyekezo, noma uthinte unjiniyela we-app."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Hlola izibuyekezo"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Unemilayezo emisha"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Vula uhlelo lokusebenza lwe-SMS ukuze ubuke"</string>
@@ -2051,8 +2051,7 @@
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Vumela ukufinyelela kwesikhathi esisodwa"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ungavumeli"</string>
<string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Amalogu edivayisi arekhoda okwenzekayo kudivayisi yakho. Ama-app angasebenzisa lawa malogu ukuze athole futhi alungise izinkinga.\n\nAmanye amalogu angase aqukathe ulwazi olubucayi, ngakho vumela ama-app owathembayo kuphela ukuthi afinyelele wonke amalogu edivayisi. \n\nUma ungayivumeli le app ukuthi ifinyelele wonke amalogu wedivayisi, isengakwazi ukufinyelela amalogu wayo. Umkhiqizi wedivayisi yakho usengakwazi ukufinyelela amanye amalogu noma ulwazi kudivayisi yakho."</string>
- <!-- no translation found for log_access_confirmation_body (7379536536425265262) -->
- <skip />
+ <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Amalogu edivayisi arekhoda okwenzekayo kudivayisi yakho. Ama-app angasebenzisa lawa malogu ukuze athole futhi alungise izinkinga.\n\nAmanye amalogu angase aqukathe ulwazi olubucayi, ngakho vumela ama-app owathembayo kuphela ukuthi afinyelele wonke amalogu edivayisi. \n\nUma ungayivumeli le app ukuthi ifinyelele wonke amalogu wedivayisi, isengakwazi ukufinyelela amalogu wayo. Umkhiqizi wedivayisi yakho usengakwazi ukufinyelela amanye amalogu noma ulwazi kudivayisi yakho.\n\nFunda kabanzi ku-g.co/android/devicelogs."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ungabonisi futhi"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"I-<xliff:g id="APP_0">%1$s</xliff:g> ifuna ukubonisa izingcezu ze-<xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Hlela"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 69c504364544..2d832bc71684 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9620,6 +9620,12 @@
<attr name="maxCollapsedHeightSmall" format="dimension" />
<!-- Whether the Drawer should be positioned at the top rather than at the bottom. -->
<attr name="showAtTop" format="boolean" />
+ <!-- By default `ResolverDrawerLayout`’s children views with `layout_ignoreOffset` property
+ set to true have a fixed position in the layout that won’t be affected by the drawer’s
+ movements. This property alternates that behavior. It specifies a child view’s id that
+ will push all ignoreOffset siblings below it when the drawer is moved i.e. setting the
+ top limit the ignoreOffset elements. -->
+ <attr name="ignoreOffsetTopLimit" format="reference" />
</declare-styleable>
<declare-styleable name="MessagingLinearLayout">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 47faf2a2ebf7..38344788a100 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -553,6 +553,14 @@
<!-- If this is true, then keep dreaming when undocking. -->
<bool name="config_keepDreamingWhenUndocking">false</bool>
+ <!-- The timeout (in ms) to wait before attempting to reconnect to the dream overlay service if
+ it becomes disconnected -->
+ <integer name="config_dreamOverlayReconnectTimeoutMs">1000</integer> <!-- 1 second -->
+ <!-- The maximum number of times to attempt reconnecting to the dream overlay service -->
+ <integer name="config_dreamOverlayMaxReconnectAttempts">3</integer>
+ <!-- The duration after which the dream overlay connection should be considered stable -->
+ <integer name="config_minDreamOverlayDurationMs">10000</integer> <!-- 10 seconds -->
+
<!-- Auto-rotation behavior -->
<!-- If true, enables auto-rotation features using the accelerometer.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b42db13e1ac2..530891f18933 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1626,6 +1626,7 @@
<java-symbol type="xml" name="password_kbd_symbols_shift" />
<java-symbol type="xml" name="power_profile" />
<java-symbol type="xml" name="power_profile_test" />
+ <java-symbol type="xml" name="irq_device_map" />
<java-symbol type="xml" name="sms_short_codes" />
<java-symbol type="xml" name="audio_assets" />
<java-symbol type="xml" name="global_keys" />
@@ -2238,6 +2239,9 @@
<java-symbol type="array" name="config_supportedDreamComplications" />
<java-symbol type="array" name="config_disabledDreamComponents" />
<java-symbol type="bool" name="config_dismissDreamOnActivityStart" />
+ <java-symbol type="integer" name="config_dreamOverlayReconnectTimeoutMs" />
+ <java-symbol type="integer" name="config_dreamOverlayMaxReconnectAttempts" />
+ <java-symbol type="integer" name="config_minDreamOverlayDurationMs" />
<java-symbol type="string" name="config_loggable_dream_prefix" />
<java-symbol type="string" name="config_dozeComponent" />
<java-symbol type="string" name="enable_explore_by_touch_warning_title" />
diff --git a/core/res/res/xml/irq_device_map.xml b/core/res/res/xml/irq_device_map.xml
new file mode 100644
index 000000000000..86a44d6a9fe0
--- /dev/null
+++ b/core/res/res/xml/irq_device_map.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2022, 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.
+*/
+-->
+<irq-device-map>
+ <!-- This file maps devices (chips) that can send IRQs to the CPU (and bring it out of sleep) to
+ logical subsystems in userspace code. Since each Android device has its own uniquely
+ designed chipset, this mapping is expected to be empty by default and should be overridden
+ by device specific configs.
+ This mapping helps the system to meaningfully attribute CPU wakeups to logical work that
+ happened on the device. The devices are referred to by their names as defined in the kernel.
+ Currently defined subsystems are:
+ - Alarm: Use this to denote wakeup alarms requested by apps via the AlarmManager API.
+
+ The overlay should use tags <device> and <subsystem> to describe this mapping in the
+ following way:
+
+ <irq-device-map>
+ <device name="device_name_1">
+ <subsystem>Subsystem1</subsystem>
+ <subsystem>Subsystem2</subsystem>
+ :
+ :
+ </device>
+ <device name="device_name_2">
+ :
+ </device>
+ :
+ </irq-device-map>
+
+ The tag <device> should have a "name" attribute specifying the kernel name of the device.
+ Each <device> tag can then enclose multiple <subsystem> tags. Each <subsystem> tag should
+ enclose the name of the logical subsystems (one of the ones defined above) as text.
+ Undefined subsystem names will be ignored by the framework.
+ -->
+</irq-device-map> \ No newline at end of file
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
index fd4fb133ef12..2719431a536e 100644
--- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
@@ -17,7 +17,6 @@
package com.android.internal.util;
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
-import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.fail;
@@ -85,12 +84,6 @@ public final class ScreenshotHelperTest {
}
@Test
- public void testSelectedRegionScreenshot() {
- mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_SELECTED_REGION,
- WindowManager.ScreenshotSource.SCREENSHOT_OTHER, mHandler, null);
- }
-
- @Test
public void testProvidedImageScreenshot() {
mScreenshotHelper.provideScreenshot(
new Bundle(), new Rect(), Insets.of(0, 0, 0, 0), 1, 1, new ComponentName("", ""),
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
index adc3676f7b93..3798da592cd5 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -34,6 +34,7 @@ android_test {
"mockito-target-minus-junit4",
"androidx.test.ext.junit",
"truth-prebuilt",
+ "servicestests-utils",
],
libs: [
diff --git a/core/tests/utiltests/src/com/android/internal/util/ObservableServiceConnectionTest.java b/core/tests/utiltests/src/com/android/internal/util/ObservableServiceConnectionTest.java
new file mode 100644
index 000000000000..d124ad9ddfb0
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/ObservableServiceConnectionTest.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2022 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.util;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.clearInvocations;
+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 android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.ObservableServiceConnection.ServiceTransformer;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayDeque;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.concurrent.Executor;
+
+@SmallTest
+public class ObservableServiceConnectionTest {
+ private static final ComponentName COMPONENT_NAME =
+ new ComponentName("test.package", "component");
+
+ public static class Foo {
+ int mValue;
+
+ Foo(int value) {
+ mValue = value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Foo)) return false;
+ Foo foo = (Foo) o;
+ return mValue == foo.mValue;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mValue);
+ }
+ }
+
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private Intent mIntent;
+ @Mock
+ private Foo mResult;
+ @Mock
+ private IBinder mBinder;
+ @Mock
+ private ServiceTransformer<Foo> mTransformer;
+ @Mock
+ private ObservableServiceConnection.Callback<Foo> mCallback;
+ private final FakeExecutor mExecutor = new FakeExecutor();
+ private ObservableServiceConnection<Foo> mConnection;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mConnection = new ObservableServiceConnection<>(
+ mContext,
+ mExecutor,
+ mTransformer,
+ mIntent,
+ /* flags= */ Context.BIND_AUTO_CREATE);
+ }
+
+ @After
+ public void tearDown() {
+ mExecutor.clearAll();
+ }
+
+ @Test
+ public void testConnect() {
+ // Register twice to ensure only one callback occurs.
+ mConnection.addCallback(mCallback);
+ mConnection.addCallback(mCallback);
+
+ mExecutor.runAll();
+ mConnection.bind();
+
+ // Ensure that no callbacks happen before connection.
+ verify(mCallback, never()).onConnected(any(), any());
+ verify(mCallback, never()).onDisconnected(any(), anyInt());
+
+ when(mTransformer.convert(mBinder)).thenReturn(mResult);
+ mConnection.onServiceConnected(COMPONENT_NAME, mBinder);
+
+ mExecutor.runAll();
+ verify(mCallback, times(1)).onConnected(mConnection, mResult);
+ }
+
+ @Test
+ public void testDisconnectBeforeBind() {
+ mConnection.addCallback(mCallback);
+ mExecutor.runAll();
+ mConnection.onServiceDisconnected(COMPONENT_NAME);
+ mExecutor.runAll();
+ // Disconnects before binds should be ignored.
+ verify(mCallback, never()).onDisconnected(eq(mConnection), anyInt());
+ }
+
+ @Test
+ public void testDisconnect() {
+ mConnection.addCallback(mCallback);
+ mExecutor.runAll();
+ mConnection.bind();
+ mConnection.onServiceDisconnected(COMPONENT_NAME);
+
+ // Ensure the callback doesn't get triggered until the executor runs.
+ verify(mCallback, never()).onDisconnected(eq(mConnection), anyInt());
+ mExecutor.runAll();
+ // Ensure proper disconnect reason reported.
+ verify(mCallback, times(1)).onDisconnected(mConnection,
+ ObservableServiceConnection.DISCONNECT_REASON_DISCONNECTED);
+ // Verify unbound from service.
+ verify(mContext, times(1)).unbindService(mConnection);
+
+ clearInvocations(mContext);
+ // Ensure unbind after disconnect has no effect on the connection
+ mConnection.unbind();
+ verify(mContext, never()).unbindService(mConnection);
+ }
+
+ @Test
+ public void testBindingDied() {
+ mConnection.addCallback(mCallback);
+ mExecutor.runAll();
+ mConnection.bind();
+ mConnection.onBindingDied(COMPONENT_NAME);
+
+ // Ensure the callback doesn't get triggered until the executor runs.
+ verify(mCallback, never()).onDisconnected(eq(mConnection), anyInt());
+ mExecutor.runAll();
+ // Ensure proper disconnect reason reported.
+ verify(mCallback, times(1)).onDisconnected(mConnection,
+ ObservableServiceConnection.DISCONNECT_REASON_BINDING_DIED);
+ // Verify unbound from service.
+ verify(mContext, times(1)).unbindService(mConnection);
+ }
+
+ @Test
+ public void testNullBinding() {
+ mConnection.addCallback(mCallback);
+ mExecutor.runAll();
+ mConnection.bind();
+ mConnection.onNullBinding(COMPONENT_NAME);
+
+ // Ensure the callback doesn't get triggered until the executor runs.
+ verify(mCallback, never()).onDisconnected(eq(mConnection), anyInt());
+ mExecutor.runAll();
+ // Ensure proper disconnect reason reported.
+ verify(mCallback, times(1)).onDisconnected(mConnection,
+ ObservableServiceConnection.DISCONNECT_REASON_NULL_BINDING);
+ // Verify unbound from service.
+ verify(mContext, times(1)).unbindService(mConnection);
+ }
+
+ @Test
+ public void testUnbind() {
+ mConnection.addCallback(mCallback);
+ mExecutor.runAll();
+ mConnection.bind();
+ mConnection.unbind();
+
+ // Ensure the callback doesn't get triggered until the executor runs.
+ verify(mCallback, never()).onDisconnected(eq(mConnection), anyInt());
+ mExecutor.runAll();
+ verify(mCallback).onDisconnected(mConnection,
+ ObservableServiceConnection.DISCONNECT_REASON_UNBIND);
+ }
+
+ static class FakeExecutor implements Executor {
+ private final Queue<Runnable> mQueue = new ArrayDeque<>();
+
+ @Override
+ public void execute(Runnable command) {
+ mQueue.add(command);
+ }
+
+ public void runAll() {
+ while (!mQueue.isEmpty()) {
+ mQueue.remove().run();
+ }
+ }
+
+ public void clearAll() {
+ while (!mQueue.isEmpty()) {
+ mQueue.remove();
+ }
+ }
+ }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/PersistentServiceConnectionTest.java b/core/tests/utiltests/src/com/android/internal/util/PersistentServiceConnectionTest.java
new file mode 100644
index 000000000000..fee46545ac62
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/PersistentServiceConnectionTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2022 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.util;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+
+import com.android.internal.util.ObservableServiceConnection.ServiceTransformer;
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+import java.util.concurrent.Executor;
+
+public class PersistentServiceConnectionTest {
+ private static final ComponentName COMPONENT_NAME =
+ new ComponentName("test.package", "component");
+ private static final int MAX_RETRIES = 2;
+ private static final int RETRY_DELAY_MS = 1000;
+ private static final int CONNECTION_MIN_DURATION_MS = 5000;
+ private PersistentServiceConnection<Proxy> mConnection;
+
+ public static class Proxy {
+ }
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private Intent mIntent;
+ @Mock
+ private Proxy mResult;
+ @Mock
+ private IBinder mBinder;
+ @Mock
+ private ServiceTransformer<Proxy> mTransformer;
+ @Mock
+ private ObservableServiceConnection.Callback<Proxy> mCallback;
+ private TestHandler mHandler;
+ private final FakeExecutor mFakeExecutor = new FakeExecutor();
+ private OffsettableClock mClock;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mClock = new OffsettableClock.Stopped();
+ mHandler = spy(new TestHandler(null, mClock));
+
+ mConnection = new PersistentServiceConnection<>(
+ mContext,
+ mFakeExecutor,
+ mHandler,
+ mTransformer,
+ mIntent,
+ /* flags= */ Context.BIND_AUTO_CREATE,
+ CONNECTION_MIN_DURATION_MS,
+ MAX_RETRIES,
+ RETRY_DELAY_MS,
+ new TestInjector(mClock));
+
+ mClock.fastForward(1000);
+ mConnection.addCallback(mCallback);
+ when(mTransformer.convert(mBinder)).thenReturn(mResult);
+ }
+
+ @After
+ public void tearDown() {
+ mFakeExecutor.clearAll();
+ }
+
+ @Test
+ public void testConnect() {
+ mConnection.bind();
+ mConnection.onServiceConnected(COMPONENT_NAME, mBinder);
+ mFakeExecutor.runAll();
+ // Ensure that we did not schedule a retry
+ verify(mHandler, never()).postDelayed(any(), anyLong());
+ }
+
+ @Test
+ public void testRetryOnBindFailure() {
+ mConnection.bind();
+
+ verify(mContext, times(1)).bindService(
+ eq(mIntent),
+ anyInt(),
+ eq(mFakeExecutor),
+ eq(mConnection));
+
+ // After disconnect, a reconnection should be attempted after the RETRY_DELAY_MS
+ mConnection.onServiceDisconnected(COMPONENT_NAME);
+ mFakeExecutor.runAll();
+ advanceTime(RETRY_DELAY_MS);
+ verify(mContext, times(2)).bindService(
+ eq(mIntent),
+ anyInt(),
+ eq(mFakeExecutor),
+ eq(mConnection));
+
+ // Reconnect attempt #2
+ mConnection.onServiceDisconnected(COMPONENT_NAME);
+ mFakeExecutor.runAll();
+ advanceTime(RETRY_DELAY_MS * 2);
+ verify(mContext, times(3)).bindService(
+ eq(mIntent),
+ anyInt(),
+ eq(mFakeExecutor),
+ eq(mConnection));
+
+ // There should be no more reconnect attempts, since the maximum is 2
+ mConnection.onServiceDisconnected(COMPONENT_NAME);
+ mFakeExecutor.runAll();
+ advanceTime(RETRY_DELAY_MS * 4);
+ verify(mContext, times(3)).bindService(
+ eq(mIntent),
+ anyInt(),
+ eq(mFakeExecutor),
+ eq(mConnection));
+ }
+
+ @Test
+ public void testManualUnbindDoesNotReconnect() {
+ mConnection.bind();
+
+ verify(mContext, times(1)).bindService(
+ eq(mIntent),
+ anyInt(),
+ eq(mFakeExecutor),
+ eq(mConnection));
+
+ mConnection.unbind();
+ // Ensure that disconnection after unbind does not reconnect.
+ mConnection.onServiceDisconnected(COMPONENT_NAME);
+ mFakeExecutor.runAll();
+ advanceTime(RETRY_DELAY_MS);
+
+ verify(mContext, times(1)).bindService(
+ eq(mIntent),
+ anyInt(),
+ eq(mFakeExecutor),
+ eq(mConnection));
+ }
+
+ private void advanceTime(long millis) {
+ mClock.fastForward(millis);
+ mHandler.timeAdvance();
+ }
+
+ static class TestInjector extends PersistentServiceConnection.Injector {
+ private final OffsettableClock mClock;
+
+ TestInjector(OffsettableClock clock) {
+ mClock = clock;
+ }
+
+ @Override
+ public long uptimeMillis() {
+ return mClock.now();
+ }
+ }
+
+ static class FakeExecutor implements Executor {
+ private final Queue<Runnable> mQueue = new ArrayDeque<>();
+
+ @Override
+ public void execute(Runnable command) {
+ mQueue.add(command);
+ }
+
+ public void runAll() {
+ while (!mQueue.isEmpty()) {
+ mQueue.remove().run();
+ }
+ }
+
+ public void clearAll() {
+ while (!mQueue.isEmpty()) {
+ mQueue.remove();
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 203ece091e46..1174b685e92c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -240,6 +240,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
/**
+ * Clears the listener set in {@link SplitController#setSplitInfoListener}.
+ */
+ @Override
+ public void clearSplitInfoCallback() {
+ synchronized (mLock) {
+ mEmbeddingCallback = null;
+ }
+ }
+
+ /**
* Called when the transaction is ready so that the organizer can update the TaskFragments based
* on the changes in transaction.
*/
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index b0b95f9cc871..4978e04e0115 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 82573b2b9acc..f615ad6e671b 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -45,6 +45,9 @@ filegroup {
"src/com/android/wm/shell/util/**/*.java",
"src/com/android/wm/shell/common/split/SplitScreenConstants.java",
"src/com/android/wm/shell/sysui/ShellSharedConstants.java",
+ "src/com/android/wm/shell/common/TransactionPool.java",
+ "src/com/android/wm/shell/animation/Interpolators.java",
+ "src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java",
],
path: "src",
}
diff --git a/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml
new file mode 100644
index 000000000000..66e5b43d76af
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml
@@ -0,0 +1,32 @@
+<!--
+ ~ Copyright (C) 2022 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="32.0dp"
+ android:height="32.0dp"
+ android:viewportWidth="32.0"
+ android:viewportHeight="32.0"
+ >
+ <group android:scaleX="0.5"
+ android:scaleY="0.5"
+ android:translateX="8.0"
+ android:translateY="8.0" >
+ <path
+ android:fillColor="@android:color/black"
+ android:pathData="MM24,40.3 L7.7,24 24,7.7 26.8,10.45 15.3,22H40.3V26H15.3L26.8,37.5Z"/>
+
+ </group>
+</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml b/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml
index 8207365a737d..53a8bb18537c 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml
@@ -15,8 +15,6 @@
~ limitations under the License.
-->
<shape android:shape="rectangle"
- android:tintMode="multiply"
- android:tint="@color/decor_caption_title_color"
xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="?android:attr/colorPrimary" />
+ <solid android:color="@android:color/white" />
</shape>
diff --git a/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml
index f2f1a1d55dee..851cbf26afc3 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml
@@ -18,15 +18,13 @@
android:width="32.0dp"
android:height="32.0dp"
android:viewportWidth="32.0"
- android:viewportHeight="32.0"
- android:tint="@color/decor_button_dark_color"
- >
+ android:viewportHeight="32.0">
<group android:scaleX="0.5"
android:scaleY="0.5"
android:translateX="8.0"
android:translateY="8.0" >
<path
- android:fillColor="@android:color/white"
- android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
+ android:fillColor="@android:color/black"
+ android:pathData="M12.45,38.35 L9.65,35.55 21.2,24 9.65,12.45 12.45,9.65 24,21.2 35.55,9.65 38.35,12.45 26.8,24 38.35,35.55 35.55,38.35 24,26.8Z"/>
</group>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
new file mode 100644
index 000000000000..ee0f4663c940
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
@@ -0,0 +1,23 @@
+<!--
+ ~ Copyright (C) 2022 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="@android:color/black" android:pathData="M3,5V3H21V5Z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml
index d183e42c173b..38cd5702f134 100644
--- a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml
+++ b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml
@@ -17,39 +17,33 @@
<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/caption"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="end"
+ android:gravity="center_horizontal"
android:background="@drawable/decor_caption_title">
<Button
- android:id="@+id/minimize_window"
- android:visibility="gone"
+ android:id="@+id/back_button"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="5dp"
android:padding="4dp"
- android:layout_gravity="top|end"
- android:contentDescription="@string/maximize_button_text"
- android:background="@drawable/decor_minimize_button_dark"
- android:duplicateParentState="true"/>
+ android:contentDescription="@string/back_button_text"
+ android:background="@drawable/decor_back_button_dark"
+ />
<Button
- android:id="@+id/maximize_window"
- android:layout_width="32dp"
+ android:id="@+id/caption_handle"
+ android:layout_width="128dp"
android:layout_height="32dp"
android:layout_margin="5dp"
android:padding="4dp"
- android:layout_gravity="center_vertical|end"
- android:contentDescription="@string/maximize_button_text"
- android:background="@drawable/decor_maximize_button_dark"
- android:duplicateParentState="true"/>
+ android:contentDescription="@string/handle_text"
+ android:background="@drawable/decor_handle_dark"/>
<Button
android:id="@+id/close_window"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="5dp"
android:padding="4dp"
- android:layout_gravity="center_vertical|end"
android:contentDescription="@string/close_button_text"
- android:background="@drawable/decor_close_button_dark"
- android:duplicateParentState="true"/>
-</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
+ android:background="@drawable/decor_close_button_dark"/>
+</com.android.wm.shell.windowdecor.WindowDecorLinearLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values-af/strings_tv.xml b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
index 6187ea46769c..77f576e79e36 100644
--- a/libs/WindowManager/Shell/res/values-af/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Skuif"</string>
<string name="pip_expand" msgid="1051966011679297308">"Vou uit"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Vou in"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Dubbeldruk "<annotation icon="home_icon">" TUIS "</annotation>" vir kontroles"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Prent-in-prent-kieslys"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Skuif links"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Skuif regs"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings_tv.xml b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
index 74ce49ef078e..fa274c35329f 100644
--- a/libs/WindowManager/Shell/res/values-am/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"ውሰድ"</string>
<string name="pip_expand" msgid="1051966011679297308">"ዘርጋ"</string>
<string name="pip_collapse" msgid="3903295106641385962">"ሰብስብ"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ለመቆጣጠሪያዎች "<annotation icon="home_icon">"መነሻ"</annotation>"ን ሁለቴ ይጫኑ"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"የስዕል-ላይ-ስዕል ምናሌ።"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ወደ ግራ ውሰድ"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ወደ ቀኝ ውሰድ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
index 9c195a7386a9..5bcca322b9fe 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"نقل"</string>
<string name="pip_expand" msgid="1051966011679297308">"توسيع"</string>
<string name="pip_collapse" msgid="3903295106641385962">"تصغير"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" انقر مرتين على "<annotation icon="home_icon">" الصفحة الرئيسية "</annotation>" للوصول لعناصر التحكم."</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"قائمة نافذة ضمن النافذة"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"نقل لليسار"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"نقل لليمين"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings_tv.xml b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
index 816b5b1c79dc..97a59cd8444b 100644
--- a/libs/WindowManager/Shell/res/values-as/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"স্থানান্তৰ কৰক"</string>
<string name="pip_expand" msgid="1051966011679297308">"বিস্তাৰ কৰক"</string>
<string name="pip_collapse" msgid="3903295106641385962">"সংকোচন কৰক"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" নিয়ন্ত্ৰণৰ বাবে "<annotation icon="home_icon">" গৃহপৃষ্ঠা "</annotation>" বুটামত দুবাৰ হেঁচক"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"চিত্ৰৰ ভিতৰৰ চিত্ৰ মেনু।"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"বাওঁফাললৈ নিয়ক"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"সোঁফাললৈ নিয়ক"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings_tv.xml b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
index ccb7a7069ad8..82deea4145bb 100644
--- a/libs/WindowManager/Shell/res/values-az/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Köçürün"</string>
<string name="pip_expand" msgid="1051966011679297308">"Genişləndirin"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Yığcamlaşdırın"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Nizamlayıcılar üçün "<annotation icon="home_icon">" ƏSAS SƏHİFƏ "</annotation>" süçimini iki dəfə basın"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Şəkildə şəkil menyusu."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Sola köçürün"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Sağa köçürün"</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
index 51a1262b1de7..6fa1310b84b1 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Premesti"</string>
<string name="pip_expand" msgid="1051966011679297308">"Proširi"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Skupi"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Dvaput pritisnite "<annotation icon="home_icon">" HOME "</annotation>" za kontrole"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Meni Slika u slici."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Pomerite nalevo"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Pomerite nadesno"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings_tv.xml b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
index 15a353c649d6..e2a1da68a886 100644
--- a/libs/WindowManager/Shell/res/values-be/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Перамясціць"</string>
<string name="pip_expand" msgid="1051966011679297308">"Разгарнуць"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Згарнуць"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Двойчы націсніце "<annotation icon="home_icon">" ГАЛОЎНЫ ЭКРАН "</annotation>" для пераходу ў налады"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Меню рэжыму \"Відарыс у відарысе\"."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Перамясціць улева"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Перамясціць управа"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
index 2b27a6927077..b823987c223e 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Преместване"</string>
<string name="pip_expand" msgid="1051966011679297308">"Разгъване"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Свиване"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" За достъп до контролите натиснете 2 пъти "<annotation icon="home_icon">"НАЧАЛО"</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Меню за функцията „Картина в картината“."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Преместване наляво"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Преместване надясно"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
index 23c8ffabeede..7309eff24014 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"সরান"</string>
<string name="pip_expand" msgid="1051966011679297308">"বড় করুন"</string>
<string name="pip_collapse" msgid="3903295106641385962">"আড়াল করুন"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" কন্ট্রোলের জন্য "<annotation icon="home_icon">" হোম "</annotation>" বোতামে ডবল প্রেস করুন"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ছবির-মধ্যে-ছবি মেনু।"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"বাঁদিকে সরান"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ডানদিকে সরান"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
index 443fd620fd65..89b3cce45104 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Premjesti"</string>
<string name="pip_expand" msgid="1051966011679297308">"Proširi"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Suzi"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Dvaput pritisnite "<annotation icon="home_icon">" POČETNI EKRAN "</annotation>" za kontrole"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Meni za način rada slika u slici."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Pomjeranje ulijevo"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Pomjeranje udesno"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
index 94ba0db7e978..5ebbf47db776 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Mou"</string>
<string name="pip_expand" msgid="1051966011679297308">"Desplega"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Replega"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Prem dos cops "<annotation icon="home_icon">" INICI "</annotation>" per accedir als controls"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú de pantalla en pantalla."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mou cap a l\'esquerra"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mou cap a la dreta"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
index 3ed85dce0433..691e79051484 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Přesunout"</string>
<string name="pip_expand" msgid="1051966011679297308">"Rozbalit"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Sbalit"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Ovládací prvky zobrazíte dvojitým stisknutím "<annotation icon="home_icon">"tlačítka plochy"</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Nabídka režimu obrazu v obraze"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Přesunout doleva"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Přesunout doprava"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings_tv.xml b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
index 09024428a825..4192aa8f0ae7 100644
--- a/libs/WindowManager/Shell/res/values-da/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Flyt"</string>
<string name="pip_expand" msgid="1051966011679297308">"Udvid"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Skjul"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Tryk to gange på "<annotation icon="home_icon">" HJEM "</annotation>" for at se indstillinger"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu for integreret billede."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Flyt til venstre"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Flyt til højre"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings_tv.xml b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
index 18535c9d9338..212eb3055b78 100644
--- a/libs/WindowManager/Shell/res/values-de/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Bewegen"</string>
<string name="pip_expand" msgid="1051966011679297308">"Maximieren"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Minimieren"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Für Steuerelemente zweimal "<annotation icon="home_icon">"STARTBILDSCHIRMTASTE"</annotation>" drücken"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menü „Bild im Bild“."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Nach links bewegen"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Nach rechts bewegen"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings_tv.xml b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
index 5f8a004b0a1f..669e3fc62fea 100644
--- a/libs/WindowManager/Shell/res/values-el/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Μετακίνηση"</string>
<string name="pip_expand" msgid="1051966011679297308">"Ανάπτυξη"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Σύμπτυξη"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Πατήστε δύο φορές "<annotation icon="home_icon">" ΑΡΧΙΚΗ ΟΘΟΝΗ "</annotation>" για στοιχεία ελέγχου"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Μενού λειτουργίας Picture-in-Picture."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Μετακίνηση αριστερά"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Μετακίνηση δεξιά"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
index 839789b22a1c..2ff51f2a6fe6 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Move"</string>
<string name="pip_expand" msgid="1051966011679297308">"Expand"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Collapse"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Double-press "<annotation icon="home_icon">" HOME "</annotation>" for controls"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Picture-in-picture menu"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Move left"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Move right"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
index 839789b22a1c..2ff51f2a6fe6 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Move"</string>
<string name="pip_expand" msgid="1051966011679297308">"Expand"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Collapse"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Double-press "<annotation icon="home_icon">" HOME "</annotation>" for controls"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Picture-in-picture menu"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Move left"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Move right"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
index 839789b22a1c..2ff51f2a6fe6 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Move"</string>
<string name="pip_expand" msgid="1051966011679297308">"Expand"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Collapse"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Double-press "<annotation icon="home_icon">" HOME "</annotation>" for controls"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Picture-in-picture menu"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Move left"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Move right"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
index 839789b22a1c..2ff51f2a6fe6 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Move"</string>
<string name="pip_expand" msgid="1051966011679297308">"Expand"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Collapse"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Double-press "<annotation icon="home_icon">" HOME "</annotation>" for controls"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Picture-in-picture menu"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Move left"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Move right"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
index 507e066e3812..d2bc489b9226 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‎‏‎‏‎‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎Move‎‏‎‎‏‎"</string>
<string name="pip_expand" msgid="1051966011679297308">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‎Expand‎‏‎‎‏‎"</string>
<string name="pip_collapse" msgid="3903295106641385962">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‏‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‎‏‎‏‎‎Collapse‎‏‎‎‏‎"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‏‏‎‏‎ Double press ‎‏‎‎‏‏‎"<annotation icon="home_icon">"‎‏‎‎‏‏‏‎ HOME ‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎ for controls‎‏‎‎‏‎"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‎‎‏‏‏‎‎Picture-in-Picture menu.‎‏‎‎‏‎"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‏‎‎‎‎‎‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‏‎Move left‎‏‎‎‏‎"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‎Move right‎‏‎‎‏‎"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
index a2c27b79e04c..2b9324d2223a 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Mover"</string>
<string name="pip_expand" msgid="1051966011679297308">"Expandir"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Contraer"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Presiona dos veces "<annotation icon="home_icon">"INICIO"</annotation>" para ver los controles"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú de pantalla en pantalla"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover hacia la izquierda"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover hacia la derecha"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings_tv.xml b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
index 75db421ec405..7a619d6108d9 100644
--- a/libs/WindowManager/Shell/res/values-es/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Mover"</string>
<string name="pip_expand" msgid="1051966011679297308">"Mostrar"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Contraer"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Pulsa dos veces "<annotation icon="home_icon">"INICIO"</annotation>" para ver los controles"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú de imagen en imagen."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover hacia la izquierda"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover hacia la derecha"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings_tv.xml b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
index e8fcb180c0c4..7b389045f302 100644
--- a/libs/WindowManager/Shell/res/values-et/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Teisalda"</string>
<string name="pip_expand" msgid="1051966011679297308">"Laienda"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Ahenda"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Nuppude nägemiseks vajutage 2 korda nuppu "<annotation icon="home_icon">"AVAKUVA"</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menüü Pilt pildis."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Teisalda vasakule"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Teisalda paremale"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
index 07d75d2de9cd..024ff7847a64 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Mugitu"</string>
<string name="pip_expand" msgid="1051966011679297308">"Zabaldu"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Tolestu"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Kontrolatzeko aukerak atzitzeko, sakatu birritan "<annotation icon="home_icon">" HASIERA "</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Pantaila txiki gainjarriaren menua."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Eraman ezkerrera"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Eraman eskuinera"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
index 03f51d01a3a8..dad584ff16c3 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"انتقال"</string>
<string name="pip_expand" msgid="1051966011679297308">"گسترده کردن"</string>
<string name="pip_collapse" msgid="3903295106641385962">"جمع کردن"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" برای کنترل‌ها، دکمه "<annotation icon="home_icon">"صفحه اصلی"</annotation>" را دوبار فشار دهید"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"منوی تصویر در تصویر."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"انتقال به‌چپ"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"انتقال به‌راست"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
index 24ab7d99e180..96e4d214dc0d 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Siirrä"</string>
<string name="pip_expand" msgid="1051966011679297308">"Laajenna"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Tiivistä"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Asetukset: paina "<annotation icon="home_icon">"ALOITUSNÄYTTÖPAINIKETTA"</annotation>" kahdesti"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Kuva kuvassa ‑valikko."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Siirrä vasemmalle"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Siirrä oikealle"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
index 87651ec711d9..6ce5da988ea2 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Déplacer"</string>
<string name="pip_expand" msgid="1051966011679297308">"Développer"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Réduire"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Appuyez deux fois sur "<annotation icon="home_icon">" ACCUEIL "</annotation>" pour les commandes"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu d\'incrustation d\'image."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Déplacer vers la gauche"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Déplacer vers la droite"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
index 37863fb82295..636d670a4bea 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Déplacer"</string>
<string name="pip_expand" msgid="1051966011679297308">"Développer"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Réduire"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Menu de commandes : appuyez deux fois sur "<annotation icon="home_icon">"ACCUEIL"</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu \"Picture-in-picture\"."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Déplacer vers la gauche"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Déplacer vers la droite"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
index 5d6de76c4deb..2d9ea88a868e 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Mover"</string>
<string name="pip_expand" msgid="1051966011679297308">"Despregar"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Contraer"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Preme "<annotation icon="home_icon">"INICIO"</annotation>" dúas veces para acceder aos controis"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú de pantalla superposta."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover cara á esquerda"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover cara á dereita"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
index 6c1b9db73582..94e36d103c58 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"ખસેડો"</string>
<string name="pip_expand" msgid="1051966011679297308">"મોટું કરો"</string>
<string name="pip_collapse" msgid="3903295106641385962">"નાનું કરો"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" નિયંત્રણો માટે "<annotation icon="home_icon">" હોમ "</annotation>" બટન પર બે વાર દબાવો"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ચિત્રમાં ચિત્ર મેનૂ."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ડાબે ખસેડો"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"જમણે ખસેડો"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
index e0227253b2dc..4f01ee3a0c80 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"ले जाएं"</string>
<string name="pip_expand" msgid="1051966011679297308">"बड़ा करें"</string>
<string name="pip_collapse" msgid="3903295106641385962">"छोटा करें"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" कंट्रोल मेन्यू पर जाने के लिए, "<annotation icon="home_icon">" होम बटन"</annotation>" दो बार दबाएं"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"पिक्चर में पिक्चर मेन्यू."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"बाईं ओर ले जाएं"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"दाईं ओर ले जाएं"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
index a09e6e805f63..197ad32621da 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Premjesti"</string>
<string name="pip_expand" msgid="1051966011679297308">"Proširi"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Sažmi"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Dvaput pritisnite "<annotation icon="home_icon">"POČETNI ZASLON"</annotation>" za kontrole"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Izbornik slike u slici."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Pomaknite ulijevo"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Pomaknite udesno"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
index 5e065c2ad4e7..181783d6a536 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Áthelyezés"</string>
<string name="pip_expand" msgid="1051966011679297308">"Kibontás"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Összecsukás"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Vezérlők: "<annotation icon="home_icon">" KEZDŐKÉPERNYŐ "</annotation>" gomb kétszer megnyomva"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Kép a képben menü."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mozgatás balra"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mozgatás jobbra"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
index 7963abf8972b..6077b80a6b4d 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Տեղափոխել"</string>
<string name="pip_expand" msgid="1051966011679297308">"Ծավալել"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Ծալել"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Կարգավորումների համար կրկնակի սեղմեք "<annotation icon="home_icon">"ԳԼԽԱՎՈՐ ԷԿՐԱՆ"</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"«Նկար նկարի մեջ» ռեժիմի ընտրացանկ։"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Տեղափոխել ձախ"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Տեղափոխել աջ"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings_tv.xml b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
index 7d37154bb86c..c1c7e9dba853 100644
--- a/libs/WindowManager/Shell/res/values-in/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Pindahkan"</string>
<string name="pip_expand" msgid="1051966011679297308">"Luaskan"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Ciutkan"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Tekan dua kali "<annotation icon="home_icon">" HOME "</annotation>" untuk membuka kontrol"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu Picture-in-Picture."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Pindahkan ke kiri"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Pindahkan ke kanan"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings_tv.xml b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
index 1490cb98e034..b51500d33568 100644
--- a/libs/WindowManager/Shell/res/values-is/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Færa"</string>
<string name="pip_expand" msgid="1051966011679297308">"Stækka"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Minnka"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Ýttu tvisvar á "<annotation icon="home_icon">" HEIM "</annotation>" til að opna stillingar"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Valmynd fyrir mynd í mynd."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Færa til vinstri"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Færa til hægri"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings_tv.xml b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
index a48516f2588e..883b58c02e7f 100644
--- a/libs/WindowManager/Shell/res/values-it/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Sposta"</string>
<string name="pip_expand" msgid="1051966011679297308">"Espandi"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Comprimi"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Premi due volte "<annotation icon="home_icon">" HOME "</annotation>" per aprire i controlli"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu Picture in picture."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Sposta a sinistra"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Sposta a destra"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
index 2af1896d3c67..2b18787acaf9 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"העברה"</string>
<string name="pip_expand" msgid="1051966011679297308">"הרחבה"</string>
<string name="pip_collapse" msgid="3903295106641385962">"כיווץ"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" לחיצה כפולה על "<annotation icon="home_icon">" הלחצן הראשי "</annotation>" תציג את אמצעי הבקרה"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"תפריט \'תמונה בתוך תמונה\'."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"הזזה שמאלה"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"הזזה ימינה"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
index bc7dcb7aa029..366ff0c1233d 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"移動"</string>
<string name="pip_expand" msgid="1051966011679297308">"開く"</string>
<string name="pip_collapse" msgid="3903295106641385962">"閉じる"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" コントロールにアクセス: "<annotation icon="home_icon">" ホーム "</annotation>" を 2 回押します"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ピクチャー イン ピクチャーのメニューです。"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"左に移動"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"右に移動"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
index 898dac2aca88..29c592415ba0 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"გადაადგილება"</string>
<string name="pip_expand" msgid="1051966011679297308">"გაშლა"</string>
<string name="pip_collapse" msgid="3903295106641385962">"ჩაკეცვა"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" მართვის საშუალებებზე წვდომისთვის ორმაგად დააჭირეთ "<annotation icon="home_icon">" მთავარ ღილაკს "</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"მენიუ „ეკრანი ეკრანში“."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"მარცხნივ გადატანა"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"მარჯვნივ გადატანა"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
index cdf564fb4ca0..d249c05ad378 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Жылжыту"</string>
<string name="pip_expand" msgid="1051966011679297308">"Жаю"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Жию"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Басқару элементтері: "<annotation icon="home_icon">" НЕГІЗГІ ЭКРАН "</annotation>" түймесін екі рет басыңыз."</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"\"Сурет ішіндегі сурет\" мәзірі."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Солға жылжыту"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Оңға жылжыту"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings_tv.xml b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
index 1a7ae813c1d3..dd203f953b24 100644
--- a/libs/WindowManager/Shell/res/values-km/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"ផ្លាស់ទី"</string>
<string name="pip_expand" msgid="1051966011679297308">"ពង្រីក"</string>
<string name="pip_collapse" msgid="3903295106641385962">"បង្រួម"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ចុចពីរដងលើ"<annotation icon="home_icon">"ប៊ូតុងដើម"</annotation>" ដើម្បីបើកផ្ទាំងគ្រប់គ្រង"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ម៉ឺនុយ​រូប​ក្នុងរូប"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ផ្លាស់ទី​ទៅ​ឆ្វេង"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ផ្លាស់ទីទៅ​ស្តាំ"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
index 45de068c80a0..05d84cb368ef 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"ಸರಿಸಿ"</string>
<string name="pip_expand" msgid="1051966011679297308">"ವಿಸ್ತೃತಗೊಳಿಸಿ"</string>
<string name="pip_collapse" msgid="3903295106641385962">"ಕುಗ್ಗಿಸಿ"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ಕಂಟ್ರೋಲ್‌ಗಳಿಗಾಗಿ "<annotation icon="home_icon">" ಹೋಮ್ "</annotation>" ಅನ್ನು ಎರಡು ಬಾರಿ ಒತ್ತಿ"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರ ಮೆನು."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ಎಡಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ಬಲಕ್ಕೆ ಸರಿಸಿ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
index 9e8f1f1258a5..aafaf07a002c 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"이동"</string>
<string name="pip_expand" msgid="1051966011679297308">"펼치기"</string>
<string name="pip_collapse" msgid="3903295106641385962">"접기"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" 제어 메뉴에 액세스하려면 "<annotation icon="home_icon">" 홈 "</annotation>"을 두 번 누르세요."</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"PIP 모드 메뉴입니다."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"왼쪽으로 이동"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"오른쪽으로 이동"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
index 19fac5876bb0..441a4f3f1dd9 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Жылдыруу"</string>
<string name="pip_expand" msgid="1051966011679297308">"Жайып көрсөтүү"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Жыйыштыруу"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Башкаруу элементтерин ачуу үчүн "<annotation icon="home_icon">" БАШКЫ БЕТ "</annotation>" баскычын эки жолу басыңыз"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Сүрөт ичиндеги сүрөт менюсу."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Солго жылдыруу"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Оңго жылдыруу"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
index 6cd0f37c516c..3939e5ca0aec 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"ຍ້າຍ"</string>
<string name="pip_expand" msgid="1051966011679297308">"ຂະຫຍາຍ"</string>
<string name="pip_collapse" msgid="3903295106641385962">"ຫຍໍ້"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ກົດ "<annotation icon="home_icon">" HOME "</annotation>" ສອງເທື່ອສຳລັບການຄວບຄຸມ"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ເມນູການສະແດງຜົນຊ້ອນກັນ."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ຍ້າຍໄປຊ້າຍ"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ຍ້າຍໄປຂວາ"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
index 52017dca2b94..7a6c65707371 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Perkelti"</string>
<string name="pip_expand" msgid="1051966011679297308">"Išskleisti"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Sutraukti"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Jei reikia valdiklių, dukart paspauskite "<annotation icon="home_icon">"PAGRINDINIS"</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Vaizdo vaizde meniu."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Perkelti kairėn"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Perkelti dešinėn"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
index 11abac6f6197..0e1ede9b00e1 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Pārvietot"</string>
<string name="pip_expand" msgid="1051966011679297308">"Izvērst"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Sakļaut"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Atvērt vadīklas: divreiz nospiediet pogu "<annotation icon="home_icon">"SĀKUMS"</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Izvēlne attēlam attēlā."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Pārvietot pa kreisi"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Pārvietot pa labi"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
index 21293223b882..a28eda5eabb7 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Премести"</string>
<string name="pip_expand" msgid="1051966011679297308">"Прошири"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Собери"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Притиснете двапати на "<annotation icon="home_icon">" HOME "</annotation>" за контроли"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Мени за „Слика во слика“."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Премести налево"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Премести надесно"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
index 549e39b21101..b31dd45f56f3 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"നീക്കുക"</string>
<string name="pip_expand" msgid="1051966011679297308">"വികസിപ്പിക്കുക"</string>
<string name="pip_collapse" msgid="3903295106641385962">"ചുരുക്കുക"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" നിയന്ത്രണങ്ങൾക്കായി "<annotation icon="home_icon">" ഹോം "</annotation>" രണ്ട് തവണ അമർത്തുക"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ചിത്രത്തിനുള്ളിൽ ചിത്രം മെനു."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ഇടത്തേക്ക് നീക്കുക"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"വലത്തേക്ക് നീക്കുക"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
index 9a85d96ca602..8a68ebc298c6 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Зөөх"</string>
<string name="pip_expand" msgid="1051966011679297308">"Дэлгэх"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Хураах"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Хяналтад хандах бол "<annotation icon="home_icon">" HOME "</annotation>" дээр хоёр дарна уу"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Дэлгэцэн доторх дэлгэцийн цэс."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Зүүн тийш зөөх"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Баруун тийш зөөх"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
index a9779b3a3e89..7a1806075529 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"हलवा"</string>
<string name="pip_expand" msgid="1051966011679297308">"विस्तार करा"</string>
<string name="pip_collapse" msgid="3903295106641385962">"कोलॅप्स करा"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" नियंत्रणांसाठी "<annotation icon="home_icon">" होम "</annotation>" दोनदा दाबा"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"चित्रात-चित्र मेनू."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"डावीकडे हलवा"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"उजवीकडे हलवा"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
index 8fe992d9f3b9..ba7caaf9c153 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Alih"</string>
<string name="pip_expand" msgid="1051966011679297308">"Kembangkan"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Kuncupkan"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Tekan dua kali "<annotation icon="home_icon">" LAMAN UTAMA "</annotation>" untuk mengakses kawalan"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu Gambar dalam Gambar."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Alih ke kiri"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Alih ke kanan"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings_tv.xml b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
index 105628d8149e..714716024a4e 100644
--- a/libs/WindowManager/Shell/res/values-my/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"ရွှေ့ရန်"</string>
<string name="pip_expand" msgid="1051966011679297308">"ချဲ့ရန်"</string>
<string name="pip_collapse" msgid="3903295106641385962">"လျှော့ပြရန်"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ထိန်းချုပ်မှုအတွက် "<annotation icon="home_icon">" ပင်မခလုတ် "</annotation>" နှစ်ချက်နှိပ်ပါ"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"နှစ်ခုထပ်၍ ကြည့်ခြင်းမီနူး။"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ဘယ်သို့ရွှေ့ရန်"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ညာသို့ရွှေ့ရန်"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
index ca63518df7a5..3e60e926c1da 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Flytt"</string>
<string name="pip_expand" msgid="1051966011679297308">"Vis"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Skjul"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Dobbelttrykk på "<annotation icon="home_icon">"HJEM"</annotation>" for å åpne kontroller"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Bilde-i-bilde-meny."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Flytt til venstre"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Flytt til høyre"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
index 7cbf9e294e7b..8a32d1244500 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"सार्नुहोस्"</string>
<string name="pip_expand" msgid="1051966011679297308">"एक्स्पान्ड गर्नुहोस्"</string>
<string name="pip_collapse" msgid="3903295106641385962">"कोल्याप्स गर्नुहोस्"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" कन्ट्रोल मेनु खोल्न "<annotation icon="home_icon">" होम "</annotation>" बटन दुई पटक थिच्नुहोस्"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"\"picture-in-picture\" मेनु।"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"बायाँतिर सार्नुहोस्"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"दायाँतिर सार्नुहोस्"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
index 2deaeddc4080..dc385f1ad2c9 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Verplaatsen"</string>
<string name="pip_expand" msgid="1051966011679297308">"Uitvouwen"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Samenvouwen"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Druk twee keer op "<annotation icon="home_icon">" HOME "</annotation>" voor bedieningselementen"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Scherm-in-scherm-menu."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Naar links verplaatsen"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Naar rechts verplaatsen"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings_tv.xml b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
index 0c1d99e4ca71..527bf5dcb452 100644
--- a/libs/WindowManager/Shell/res/values-or/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"ମୁଭ କରନ୍ତୁ"</string>
<string name="pip_expand" msgid="1051966011679297308">"ବିସ୍ତାର କରନ୍ତୁ"</string>
<string name="pip_collapse" msgid="3903295106641385962">"ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ପାଇଁ "<annotation icon="home_icon">" ହୋମ ବଟନ "</annotation>"କୁ ଦୁଇଥର ଦବାନ୍ତୁ"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ପିକଚର-ଇନ-ପିକଚର ମେନୁ।"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
index a1edde738775..758948d907df 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"ਲਿਜਾਓ"</string>
<string name="pip_expand" msgid="1051966011679297308">"ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="pip_collapse" msgid="3903295106641385962">"ਸਮੇਟੋ"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ਕੰਟਰੋਲਾਂ ਲਈ "<annotation icon="home_icon">" ਹੋਮ ਬਟਨ "</annotation>" ਨੂੰ ਦੋ ਵਾਰ ਦਬਾਓ"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ਤਸਵੀਰ-ਵਿੱਚ-ਤਸਵੀਰ ਮੀਨੂ।"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ਖੱਬੇ ਲਿਜਾਓ"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ਸੱਜੇ ਲਿਜਾਓ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
index 2bb90addc241..3c0bcd7903f3 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Przenieś"</string>
<string name="pip_expand" msgid="1051966011679297308">"Rozwiń"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Zwiń"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Naciśnij dwukrotnie "<annotation icon="home_icon">"EKRAN GŁÓWNY"</annotation>", aby wyświetlić ustawienia"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu funkcji Obraz w obrazie."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Przenieś w lewo"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Przenieś w prawo"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
index 14d1c34fd3e8..37f657350e85 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Mover"</string>
<string name="pip_expand" msgid="1051966011679297308">"Abrir"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Fechar"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Pressione o botão "<annotation icon="home_icon">"home"</annotation>" duas vezes para acessar os controles"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu do picture-in-picture"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover para a esquerda"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover para a direita"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
index 1ada4508714a..4c61370de892 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Mover"</string>
<string name="pip_expand" msgid="1051966011679297308">"Expandir"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Reduzir"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Prima duas vezes "<annotation icon="home_icon">" PÁGINA INICIAL "</annotation>" para controlos"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu de ecrã no ecrã."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover para a esquerda"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover para a direita"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
index 14d1c34fd3e8..37f657350e85 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Mover"</string>
<string name="pip_expand" msgid="1051966011679297308">"Abrir"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Fechar"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Pressione o botão "<annotation icon="home_icon">"home"</annotation>" duas vezes para acessar os controles"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu do picture-in-picture"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover para a esquerda"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover para a direita"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
index b5245ffbf0bc..d652ed9ed03b 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Mută"</string>
<string name="pip_expand" msgid="1051966011679297308">"Extinde"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Restrânge"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Apasă de două ori "<annotation icon="home_icon">"butonul ecran de pornire"</annotation>" pentru comenzi"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Meniu picture-in-picture."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mută la stânga"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mută la dreapta"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
index e7f55ec1bc57..12b73691fc80 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Переместить"</string>
<string name="pip_expand" msgid="1051966011679297308">"Развернуть"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Свернуть"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Элементы управления: дважды нажмите "<annotation icon="home_icon">" кнопку главного экрана "</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Меню \"Картинка в картинке\"."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Переместить влево"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Переместить вправо"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings_tv.xml b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
index 5478ce5d3d40..3b1eab253808 100644
--- a/libs/WindowManager/Shell/res/values-si/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"ගෙන යන්න"</string>
<string name="pip_expand" msgid="1051966011679297308">"දිග හරින්න"</string>
<string name="pip_collapse" msgid="3903295106641385962">"හකුළන්න"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" පාලන සඳහා "<annotation icon="home_icon">" මුල් පිටුව "</annotation>" දෙවරක් ඔබන්න"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"පින්තූරය තුළ පින්තූරය මෙනුව"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"වමට ගෙන යන්න"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"දකුණට ගෙන යන්න"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
index 1df43afca2da..0dadd2f14b08 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Presunúť"</string>
<string name="pip_expand" msgid="1051966011679297308">"Rozbaliť"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Zbaliť"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Ovládanie zobraz. dvoj. stlač. "<annotation icon="home_icon">" TLAČIDLA PLOCHY "</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Ponuka obrazu v obraze."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Posunúť doľava"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Posunúť doprava"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
index 88fc8325aa01..9e5903f71cf2 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Premakni"</string>
<string name="pip_expand" msgid="1051966011679297308">"Razširi"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Strni"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Za kontrolnike dvakrat pritisnite gumb za "<annotation icon="home_icon">" ZAČETNI ZASLON "</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Meni za sliko v sliki"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Premakni levo"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Premakni desno"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
index 58687e5867fe..2612e5e20c0f 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Lëviz"</string>
<string name="pip_expand" msgid="1051966011679297308">"Zgjero"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Palos"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Trokit dy herë "<annotation icon="home_icon">" KREU "</annotation>" për kontrollet"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menyja e \"Figurës brenda figurës\"."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Lëviz majtas"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Lëviz djathtas"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
index e850979174a3..5c2fb5eecf50 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Премести"</string>
<string name="pip_expand" msgid="1051966011679297308">"Прошири"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Скупи"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Двапут притисните "<annotation icon="home_icon">" HOME "</annotation>" за контроле"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Мени Слика у слици."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Померите налево"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Померите надесно"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
index d3a9c3de66db..afd04eb741e7 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Flytta"</string>
<string name="pip_expand" msgid="1051966011679297308">"Utöka"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Komprimera"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Tryck snabbt två gånger på "<annotation icon="home_icon">" HEM "</annotation>" för kontroller"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Bild-i-bild-meny."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Flytta åt vänster"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Flytta åt höger"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
index 7b9a310ff0b6..f7e391e3b9d7 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Hamisha"</string>
<string name="pip_expand" msgid="1051966011679297308">"Panua"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Kunja"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Bonyeza mara mbili kitufe cha "<annotation icon="home_icon">" UKURASA WA KWANZA "</annotation>" kupata vidhibiti"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menyu ya kipengele cha kupachika picha ndani ya picha nyingine."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Sogeza kushoto"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Sogeza kulia"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
index e201401e2e35..6fe3311827f9 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"நகர்த்து"</string>
<string name="pip_expand" msgid="1051966011679297308">"விரி"</string>
<string name="pip_collapse" msgid="3903295106641385962">"சுருக்கு"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" கட்டுப்பாடுகள்: "<annotation icon="home_icon">" முகப்பு "</annotation>" பட்டனை இருமுறை அழுத்துக"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"பிக்ச்சர்-இன்-பிக்ச்சர் மெனு."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"இடப்புறம் நகர்த்து"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"வலப்புறம் நகர்த்து"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index ef0f9e7a6cff..fd7c18fb8c67 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -29,7 +29,7 @@
<string name="pip_pause" msgid="690688849510295232">"పాజ్ చేయి"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"దాటవేసి తర్వాత దానికి వెళ్లు"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"దాటవేసి మునుపటి దానికి వెళ్లు"</string>
- <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"పరిమాణం మార్చు"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"సైజ్‌ మార్చు"</string>
<string name="accessibility_action_pip_stash" msgid="4060775037619702641">"స్టాచ్"</string>
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ఆన్‌స్టాచ్"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"స్క్రీన్ విభజనతో యాప్‌ పని చేయకపోవచ్చు."</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings_tv.xml b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
index 6284d90cb11f..1c86e906c6e1 100644
--- a/libs/WindowManager/Shell/res/values-te/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"తరలించండి"</string>
<string name="pip_expand" msgid="1051966011679297308">"విస్తరించండి"</string>
<string name="pip_collapse" msgid="3903295106641385962">"కుదించండి"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" కంట్రోల్స్ కోసం "<annotation icon="home_icon">" HOME "</annotation>" బటన్ రెండుసార్లు నొక్కండి"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"పిక్చర్-ఇన్-పిక్చర్ మెనూ."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ఎడమ వైపుగా జరపండి"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"కుడి వైపుగా జరపండి"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings_tv.xml b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
index 27cf56c6e154..b37f0345eaf9 100644
--- a/libs/WindowManager/Shell/res/values-th/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"ย้าย"</string>
<string name="pip_expand" msgid="1051966011679297308">"ขยาย"</string>
<string name="pip_collapse" msgid="3903295106641385962">"ยุบ"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" กดปุ่ม "<annotation icon="home_icon">" หน้าแรก "</annotation>" สองครั้งเพื่อเปิดการควบคุม"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"เมนูการแสดงภาพซ้อนภาพ"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ย้ายไปทางซ้าย"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ย้ายไปทางขวา"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
index 4cc050bebe5b..0ea5d341d380 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Ilipat"</string>
<string name="pip_expand" msgid="1051966011679297308">"I-expand"</string>
<string name="pip_collapse" msgid="3903295106641385962">"I-collapse"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" I-double press ang "<annotation icon="home_icon">" HOME "</annotation>" para sa mga kontrol"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu ng Picture-in-Picture."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Ilipat pakaliwa"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Ilipat pakanan"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
index 69bb608061e4..7550aec4ad7a 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Taşı"</string>
<string name="pip_expand" msgid="1051966011679297308">"Genişlet"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Daralt"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Kontroller için "<annotation icon="home_icon">" ANA SAYFA "</annotation>" düğmesine iki kez basın"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Pencere içinde pencere menüsü."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Sola taşı"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Sağa taşı"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
index 81a8285c58cf..3a4c68d30700 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Перемістити"</string>
<string name="pip_expand" msgid="1051966011679297308">"Розгорнути"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Згорнути"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Відкрити елементи керування: двічі натисніть "<annotation icon="home_icon">"HOME"</annotation></string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Меню \"картинка в картинці\""</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Перемістити ліворуч"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Перемістити праворуч"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
index e83885772f2d..87018b1a9c47 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"منتقل کریں"</string>
<string name="pip_expand" msgid="1051966011679297308">"پھیلائیں"</string>
<string name="pip_collapse" msgid="3903295106641385962">"سکیڑیں"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" کنٹرولز کے لیے "<annotation icon="home_icon">"ہوم "</annotation>" بٹن کو دو بار دبائیں"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"تصویر میں تصویر کا مینو۔"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"دائیں منتقل کریں"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"بائیں منتقل کریں"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
index da953356628c..86ceea1f6455 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Boshqa joyga olish"</string>
<string name="pip_expand" msgid="1051966011679297308">"Yoyish"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Yopish"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Boshqaruv uchun "<annotation icon="home_icon">"ASOSIY"</annotation>" tugmani ikki marta bosing"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Tasvir ustida tasvir menyusi."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Chapga olish"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Oʻngga olish"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
index 1f9260fdcff0..b5686627f04c 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Di chuyển"</string>
<string name="pip_expand" msgid="1051966011679297308">"Mở rộng"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Thu gọn"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Nhấn đúp vào nút "<annotation icon="home_icon">" MÀN HÌNH CHÍNH "</annotation>" để mở trình đơn điều khiển"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Trình đơn hình trong hình."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Di chuyển sang trái"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Di chuyển sang phải"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
index 399d639fe70f..8ac540463ab1 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"移动"</string>
<string name="pip_expand" msgid="1051966011679297308">"展开"</string>
<string name="pip_collapse" msgid="3903295106641385962">"收起"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" 按两次"<annotation icon="home_icon">"主屏幕"</annotation>"按钮可查看相关控件"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"画中画菜单。"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"左移"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"右移"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
index acbc26d033cd..e4315b6819a0 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"移動"</string>
<string name="pip_expand" msgid="1051966011679297308">"展開"</string>
<string name="pip_collapse" msgid="3903295106641385962">"收合"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" 按兩下"<annotation icon="home_icon">" 主畫面按鈕"</annotation>"即可顯示控制項"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"畫中畫選單。"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"向左移"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"向右移"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
index f8c683ec3a60..866aa452b071 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"移動"</string>
<string name="pip_expand" msgid="1051966011679297308">"展開"</string>
<string name="pip_collapse" msgid="3903295106641385962">"收合"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" 按兩下"<annotation icon="home_icon">"主畫面按鈕"</annotation>"即可顯示控制選項"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"子母畫面選單。"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"向左移動"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"向右移動"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
index 20243a9dfc9c..4ff14e8b13dd 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
@@ -24,7 +24,8 @@
<string name="pip_move" msgid="158770205886688553">"Hambisa"</string>
<string name="pip_expand" msgid="1051966011679297308">"Nweba"</string>
<string name="pip_collapse" msgid="3903295106641385962">"Goqa"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Chofoza kabili "<annotation icon="home_icon">" IKHAYA"</annotation>" mayelana nezilawuli"</string>
+ <!-- no translation found for pip_edu_text (7930546669915337998) -->
+ <skip />
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Imenyu yesithombe-esithombeni"</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Yisa kwesokunxele"</string>
<string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Yisa kwesokudla"</string>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index b48a508fddb8..4807f08b4bed 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -198,4 +198,8 @@
<string name="minimize_button_text">Minimize</string>
<!-- Accessibility text for the close window button [CHAR LIMIT=NONE] -->
<string name="close_button_text">Close</string>
+ <!-- Accessibility text for the caption back button [CHAR LIMIT=NONE] -->
+ <string name="back_button_text">Back</string>
+ <!-- Accessibility text for the caption handle [CHAR LIMIT=NONE] -->
+ <string name="handle_text">Handle</string>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 419e62daf586..c2ad1a98d167 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -118,6 +118,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private boolean mFreezeDividerWindow = false;
private int mOrientation;
private int mRotation;
+ private int mDensity;
private final boolean mDimNonImeSide;
@@ -290,9 +291,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
final int rotation = configuration.windowConfiguration.getRotation();
final Rect rootBounds = configuration.windowConfiguration.getBounds();
final int orientation = configuration.orientation;
+ final int density = configuration.densityDpi;
if (mOrientation == orientation
&& mRotation == rotation
+ && mDensity == density
&& mRootBounds.equals(rootBounds)) {
return false;
}
@@ -303,6 +306,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mTempRect.set(mRootBounds);
mRootBounds.set(rootBounds);
mRotation = rotation;
+ mDensity = density;
mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null);
initDividerPosition(mTempRect);
updateInvisibleRect();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 1ec98d3e94f3..f1670cd792cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -191,13 +191,13 @@ public abstract class WMShellModule {
SyncTransactionQueue syncQueue,
@DynamicOverride DesktopModeController desktopModeController) {
return new CaptionWindowDecorViewModel(
- context,
- mainHandler,
- mainChoreographer,
- taskOrganizer,
- displayController,
- syncQueue,
- desktopModeController);
+ context,
+ mainHandler,
+ mainChoreographer,
+ taskOrganizer,
+ displayController,
+ syncQueue,
+ desktopModeController);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index a918559d897e..30124a5363a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -161,6 +161,10 @@ public class PipController implements PipTransitionController.PipTransitionCallb
// early bail out if the keep clear areas feature is disabled
return;
}
+ if (mPipBoundsState.isStashed()) {
+ // don't move when stashed
+ return;
+ }
// if there is another animation ongoing, wait for it to finish and try again
if (mPipAnimationController.isAnimating()) {
mMainExecutor.removeCallbacks(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index 014f02bcf8b7..8bba44049c88 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -15,38 +15,20 @@
*/
package com.android.wm.shell.startingsurface;
-import static android.view.Choreographer.CALLBACK_COMMIT;
import static android.view.View.GONE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLASHSCREEN_EXIT_ANIM;
import android.animation.Animator;
-import android.animation.ValueAnimator;
import android.content.Context;
-import android.graphics.BlendMode;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.RadialGradient;
import android.graphics.Rect;
-import android.graphics.Shader;
-import android.util.MathUtils;
import android.util.Slog;
-import android.view.Choreographer;
import android.view.SurfaceControl;
-import android.view.SyncRtSurfaceTransactionApplier;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
import android.window.SplashScreenView;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.R;
-import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.TransactionPool;
/**
@@ -55,14 +37,8 @@ import com.android.wm.shell.common.TransactionPool;
*/
public class SplashScreenExitAnimation implements Animator.AnimatorListener {
private static final boolean DEBUG_EXIT_ANIMATION = false;
- private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
private static final String TAG = StartingWindowController.TAG;
- private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f);
- private static final Interpolator MASK_RADIUS_INTERPOLATOR =
- new PathInterpolator(0f, 0f, 0.4f, 1f);
- private static final Interpolator SHIFT_UP_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
-
private final SurfaceControl mFirstWindowSurface;
private final Rect mFirstWindowFrame = new Rect();
private final SplashScreenView mSplashScreenView;
@@ -75,9 +51,6 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
private final float mBrandingStartAlpha;
private final TransactionPool mTransactionPool;
- private ValueAnimator mMainAnimator;
- private ShiftUpAnimation mShiftUpAnimation;
- private RadialVanishAnimation mRadialVanishAnimation;
private Runnable mFinishCallback;
SplashScreenExitAnimation(Context context, SplashScreenView view, SurfaceControl leash,
@@ -121,187 +94,10 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
}
void startAnimations() {
- mMainAnimator = createAnimator();
- mMainAnimator.start();
- }
-
- // fade out icon, reveal app, shift up main window
- private ValueAnimator createAnimator() {
- // reveal app
- final float transparentRatio = 0.8f;
- final int globalHeight = mSplashScreenView.getHeight();
- final int verticalCircleCenter = 0;
- final int finalVerticalLength = globalHeight - verticalCircleCenter;
- final int halfWidth = mSplashScreenView.getWidth() / 2;
- final int endRadius = (int) (0.5 + (1f / transparentRatio * (int)
- Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth)));
- final int[] colors = {Color.WHITE, Color.WHITE, Color.TRANSPARENT};
- final float[] stops = {0f, transparentRatio, 1f};
-
- mRadialVanishAnimation = new RadialVanishAnimation(mSplashScreenView);
- mRadialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter);
- mRadialVanishAnimation.setRadius(0 /* initRadius */, endRadius);
- mRadialVanishAnimation.setRadialPaintParam(colors, stops);
-
- if (mFirstWindowSurface != null && mFirstWindowSurface.isValid()) {
- // shift up main window
- View occludeHoleView = new View(mSplashScreenView.getContext());
- if (DEBUG_EXIT_ANIMATION_BLEND) {
- occludeHoleView.setBackgroundColor(Color.BLUE);
- } else {
- occludeHoleView.setBackgroundColor(mSplashScreenView.getInitBackgroundColor());
- }
- final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
- WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength);
- mSplashScreenView.addView(occludeHoleView, params);
-
- mShiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength, occludeHoleView);
- }
-
- ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
- animator.setDuration(mAnimationDuration);
- animator.setInterpolator(Interpolators.LINEAR);
- animator.addListener(this);
- animator.addUpdateListener(a -> onAnimationProgress((float) a.getAnimatedValue()));
- return animator;
- }
-
- private static class RadialVanishAnimation extends View {
- private final SplashScreenView mView;
- private int mInitRadius;
- private int mFinishRadius;
-
- private final Point mCircleCenter = new Point();
- private final Matrix mVanishMatrix = new Matrix();
- private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
- RadialVanishAnimation(SplashScreenView target) {
- super(target.getContext());
- mView = target;
- mView.addView(this);
- mVanishPaint.setAlpha(0);
- }
-
- void onAnimationProgress(float linearProgress) {
- if (mVanishPaint.getShader() == null) {
- return;
- }
-
- final float radiusProgress = MASK_RADIUS_INTERPOLATOR.getInterpolation(linearProgress);
- final float alphaProgress = Interpolators.ALPHA_OUT.getInterpolation(linearProgress);
- final float scale = mInitRadius + (mFinishRadius - mInitRadius) * radiusProgress;
-
- mVanishMatrix.setScale(scale, scale);
- mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y);
- mVanishPaint.getShader().setLocalMatrix(mVanishMatrix);
- mVanishPaint.setAlpha(Math.round(0xFF * alphaProgress));
-
- postInvalidate();
- }
-
- void setRadius(int initRadius, int finishRadius) {
- if (DEBUG_EXIT_ANIMATION) {
- Slog.v(TAG, "RadialVanishAnimation setRadius init: " + initRadius
- + " final " + finishRadius);
- }
- mInitRadius = initRadius;
- mFinishRadius = finishRadius;
- }
-
- void setCircleCenter(int x, int y) {
- if (DEBUG_EXIT_ANIMATION) {
- Slog.v(TAG, "RadialVanishAnimation setCircleCenter x: " + x + " y " + y);
- }
- mCircleCenter.set(x, y);
- }
-
- void setRadialPaintParam(int[] colors, float[] stops) {
- // setup gradient shader
- final RadialGradient rShader =
- new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP);
- mVanishPaint.setShader(rShader);
- if (!DEBUG_EXIT_ANIMATION_BLEND) {
- // We blend the reveal gradient with the splash screen using DST_OUT so that the
- // splash screen is fully visible when radius = 0 (or gradient opacity is 0) and
- // fully invisible when radius = finishRadius AND gradient opacity is 1.
- mVanishPaint.setBlendMode(BlendMode.DST_OUT);
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint);
- }
- }
-
- private final class ShiftUpAnimation {
- private final float mFromYDelta;
- private final float mToYDelta;
- private final View mOccludeHoleView;
- private final SyncRtSurfaceTransactionApplier mApplier;
- private final Matrix mTmpTransform = new Matrix();
-
- ShiftUpAnimation(float fromYDelta, float toYDelta, View occludeHoleView) {
- mFromYDelta = fromYDelta;
- mToYDelta = toYDelta;
- mOccludeHoleView = occludeHoleView;
- mApplier = new SyncRtSurfaceTransactionApplier(occludeHoleView);
- }
-
- void onAnimationProgress(float linearProgress) {
- if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()
- || !mSplashScreenView.isAttachedToWindow()) {
- return;
- }
-
- final float progress = SHIFT_UP_INTERPOLATOR.getInterpolation(linearProgress);
- final float dy = mFromYDelta + (mToYDelta - mFromYDelta) * progress;
-
- mOccludeHoleView.setTranslationY(dy);
- mTmpTransform.setTranslate(0 /* dx */, dy);
-
- // set the vsyncId to ensure the transaction doesn't get applied too early.
- final SurfaceControl.Transaction tx = mTransactionPool.acquire();
- tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
- mTmpTransform.postTranslate(mFirstWindowFrame.left,
- mFirstWindowFrame.top + mMainWindowShiftLength);
-
- SyncRtSurfaceTransactionApplier.SurfaceParams
- params = new SyncRtSurfaceTransactionApplier.SurfaceParams
- .Builder(mFirstWindowSurface)
- .withMatrix(mTmpTransform)
- .withMergeTransaction(tx)
- .build();
- mApplier.scheduleApply(params);
-
- mTransactionPool.release(tx);
- }
-
- void finish() {
- if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
- return;
- }
- final SurfaceControl.Transaction tx = mTransactionPool.acquire();
- if (mSplashScreenView.isAttachedToWindow()) {
- tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
-
- SyncRtSurfaceTransactionApplier.SurfaceParams
- params = new SyncRtSurfaceTransactionApplier.SurfaceParams
- .Builder(mFirstWindowSurface)
- .withWindowCrop(null)
- .withMergeTransaction(tx)
- .build();
- mApplier.scheduleApply(params);
- } else {
- tx.setWindowCrop(mFirstWindowSurface, null);
- tx.apply();
- }
- mTransactionPool.release(tx);
-
- Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT,
- mFirstWindowSurface::release, null);
- }
+ SplashScreenExitAnimationUtils.startAnimations(mSplashScreenView, mFirstWindowSurface,
+ mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame, mAnimationDuration,
+ mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha, mAppRevealDelay,
+ mAppRevealDuration, this);
}
private void reset() {
@@ -316,9 +112,6 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
mFinishCallback = null;
}
}
- if (mShiftUpAnimation != null) {
- mShiftUpAnimation.finish();
- }
}
@Override
@@ -342,40 +135,4 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
public void onAnimationRepeat(Animator animation) {
// ignore
}
-
- private void onFadeOutProgress(float linearProgress) {
- final float iconProgress = ICON_INTERPOLATOR.getInterpolation(
- getProgress(linearProgress, 0 /* delay */, mIconFadeOutDuration));
- final View iconView = mSplashScreenView.getIconView();
- final View brandingView = mSplashScreenView.getBrandingView();
- if (iconView != null) {
- iconView.setAlpha(mIconStartAlpha * (1 - iconProgress));
- }
- if (brandingView != null) {
- brandingView.setAlpha(mBrandingStartAlpha * (1 - iconProgress));
- }
- }
-
- private void onAnimationProgress(float linearProgress) {
- onFadeOutProgress(linearProgress);
-
- final float revealLinearProgress = getProgress(linearProgress, mAppRevealDelay,
- mAppRevealDuration);
-
- if (mRadialVanishAnimation != null) {
- mRadialVanishAnimation.onAnimationProgress(revealLinearProgress);
- }
-
- if (mShiftUpAnimation != null) {
- mShiftUpAnimation.onAnimationProgress(revealLinearProgress);
- }
- }
-
- private float getProgress(float linearProgress, long delay, long duration) {
- return MathUtils.constrain(
- (linearProgress * (mAnimationDuration) - delay) / duration,
- 0.0f,
- 1.0f
- );
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
new file mode 100644
index 000000000000..3098e55ec78b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.startingsurface;
+
+import static android.view.Choreographer.CALLBACK_COMMIT;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.BlendMode;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.util.MathUtils;
+import android.util.Slog;
+import android.view.Choreographer;
+import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.window.SplashScreenView;
+
+import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.TransactionPool;
+
+/**
+ * Utilities for creating the splash screen window animations.
+ * @hide
+ */
+public class SplashScreenExitAnimationUtils {
+ private static final boolean DEBUG_EXIT_ANIMATION = false;
+ private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
+ private static final String TAG = "SplashScreenExitAnimationUtils";
+
+ private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f);
+ private static final Interpolator MASK_RADIUS_INTERPOLATOR =
+ new PathInterpolator(0f, 0f, 0.4f, 1f);
+ private static final Interpolator SHIFT_UP_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
+
+ /**
+ * Creates and starts the animator to fade out the icon, reveal the app, and shift up main
+ * window.
+ * @hide
+ */
+ public static void startAnimations(ViewGroup splashScreenView,
+ SurfaceControl firstWindowSurface, int mainWindowShiftLength,
+ TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
+ int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
+ int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) {
+ ValueAnimator animator =
+ createAnimator(splashScreenView, firstWindowSurface, mainWindowShiftLength,
+ transactionPool, firstWindowFrame, animationDuration, iconFadeOutDuration,
+ iconStartAlpha, brandingStartAlpha, appRevealDelay, appRevealDuration,
+ animatorListener);
+ animator.start();
+ }
+
+ /**
+ * Creates the animator to fade out the icon, reveal the app, and shift up main window.
+ * @hide
+ */
+ private static ValueAnimator createAnimator(ViewGroup splashScreenView,
+ SurfaceControl firstWindowSurface, int mMainWindowShiftLength,
+ TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
+ int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
+ int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) {
+ // reveal app
+ final float transparentRatio = 0.8f;
+ final int globalHeight = splashScreenView.getHeight();
+ final int verticalCircleCenter = 0;
+ final int finalVerticalLength = globalHeight - verticalCircleCenter;
+ final int halfWidth = splashScreenView.getWidth() / 2;
+ final int endRadius = (int) (0.5 + (1f / transparentRatio * (int)
+ Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth)));
+ final int[] colors = {Color.WHITE, Color.WHITE, Color.TRANSPARENT};
+ final float[] stops = {0f, transparentRatio, 1f};
+
+ RadialVanishAnimation radialVanishAnimation = new RadialVanishAnimation(splashScreenView);
+ radialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter);
+ radialVanishAnimation.setRadius(0 /* initRadius */, endRadius);
+ radialVanishAnimation.setRadialPaintParam(colors, stops);
+
+ View occludeHoleView = null;
+ ShiftUpAnimation shiftUpAnimation = null;
+ if (firstWindowSurface != null && firstWindowSurface.isValid()) {
+ // shift up main window
+ occludeHoleView = new View(splashScreenView.getContext());
+ if (DEBUG_EXIT_ANIMATION_BLEND) {
+ occludeHoleView.setBackgroundColor(Color.BLUE);
+ } else if (splashScreenView instanceof SplashScreenView) {
+ occludeHoleView.setBackgroundColor(
+ ((SplashScreenView) splashScreenView).getInitBackgroundColor());
+ } else {
+ occludeHoleView.setBackgroundColor(
+ isDarkTheme(splashScreenView.getContext()) ? Color.BLACK : Color.WHITE);
+ }
+ final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
+ WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength);
+ splashScreenView.addView(occludeHoleView, params);
+
+ shiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength, occludeHoleView,
+ firstWindowSurface, splashScreenView, transactionPool, firstWindowFrame,
+ mMainWindowShiftLength);
+ }
+
+ ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+ animator.setDuration(animationDuration);
+ animator.setInterpolator(Interpolators.LINEAR);
+ if (animatorListener != null) {
+ animator.addListener(animatorListener);
+ }
+ View finalOccludeHoleView = occludeHoleView;
+ ShiftUpAnimation finalShiftUpAnimation = shiftUpAnimation;
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (finalShiftUpAnimation != null) {
+ finalShiftUpAnimation.finish();
+ }
+ splashScreenView.removeView(radialVanishAnimation);
+ splashScreenView.removeView(finalOccludeHoleView);
+ }
+ });
+ animator.addUpdateListener(animation -> {
+ float linearProgress = (float) animation.getAnimatedValue();
+
+ // Fade out progress
+ final float iconProgress =
+ ICON_INTERPOLATOR.getInterpolation(getProgress(
+ linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration));
+ View iconView = null;
+ View brandingView = null;
+ if (splashScreenView instanceof SplashScreenView) {
+ iconView = ((SplashScreenView) splashScreenView).getIconView();
+ brandingView = ((SplashScreenView) splashScreenView).getBrandingView();
+ }
+ if (iconView != null) {
+ iconView.setAlpha(iconStartAlpha * (1 - iconProgress));
+ }
+ if (brandingView != null) {
+ brandingView.setAlpha(brandingStartAlpha * (1 - iconProgress));
+ }
+
+ final float revealLinearProgress = getProgress(linearProgress, appRevealDelay,
+ appRevealDuration, animationDuration);
+
+ radialVanishAnimation.onAnimationProgress(revealLinearProgress);
+
+ if (finalShiftUpAnimation != null) {
+ finalShiftUpAnimation.onAnimationProgress(revealLinearProgress);
+ }
+ });
+ return animator;
+ }
+
+ private static float getProgress(float linearProgress, long delay, long duration,
+ int animationDuration) {
+ return MathUtils.constrain(
+ (linearProgress * (animationDuration) - delay) / duration,
+ 0.0f,
+ 1.0f
+ );
+ }
+
+ private static boolean isDarkTheme(Context context) {
+ Configuration configuration = context.getResources().getConfiguration();
+ int nightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ return nightMode == Configuration.UI_MODE_NIGHT_YES;
+ }
+
+ /**
+ * View which creates a circular reveal of the underlying view.
+ * @hide
+ */
+ @SuppressLint("ViewConstructor")
+ public static class RadialVanishAnimation extends View {
+ private final ViewGroup mView;
+ private int mInitRadius;
+ private int mFinishRadius;
+
+ private final Point mCircleCenter = new Point();
+ private final Matrix mVanishMatrix = new Matrix();
+ private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+ public RadialVanishAnimation(ViewGroup target) {
+ super(target.getContext());
+ mView = target;
+ mView.addView(this);
+ if (getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
+ ((ViewGroup.MarginLayoutParams) getLayoutParams()).setMargins(0, 0, 0, 0);
+ }
+ mVanishPaint.setAlpha(0);
+ }
+
+ void onAnimationProgress(float linearProgress) {
+ if (mVanishPaint.getShader() == null) {
+ return;
+ }
+
+ final float radiusProgress = MASK_RADIUS_INTERPOLATOR.getInterpolation(linearProgress);
+ final float alphaProgress = Interpolators.ALPHA_OUT.getInterpolation(linearProgress);
+ final float scale = mInitRadius + (mFinishRadius - mInitRadius) * radiusProgress;
+
+ mVanishMatrix.setScale(scale, scale);
+ mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y);
+ mVanishPaint.getShader().setLocalMatrix(mVanishMatrix);
+ mVanishPaint.setAlpha(Math.round(0xFF * alphaProgress));
+
+ postInvalidate();
+ }
+
+ void setRadius(int initRadius, int finishRadius) {
+ if (DEBUG_EXIT_ANIMATION) {
+ Slog.v(TAG, "RadialVanishAnimation setRadius init: " + initRadius
+ + " final " + finishRadius);
+ }
+ mInitRadius = initRadius;
+ mFinishRadius = finishRadius;
+ }
+
+ void setCircleCenter(int x, int y) {
+ if (DEBUG_EXIT_ANIMATION) {
+ Slog.v(TAG, "RadialVanishAnimation setCircleCenter x: " + x + " y " + y);
+ }
+ mCircleCenter.set(x, y);
+ }
+
+ void setRadialPaintParam(int[] colors, float[] stops) {
+ // setup gradient shader
+ final RadialGradient rShader =
+ new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP);
+ mVanishPaint.setShader(rShader);
+ if (!DEBUG_EXIT_ANIMATION_BLEND) {
+ // We blend the reveal gradient with the splash screen using DST_OUT so that the
+ // splash screen is fully visible when radius = 0 (or gradient opacity is 0) and
+ // fully invisible when radius = finishRadius AND gradient opacity is 1.
+ mVanishPaint.setBlendMode(BlendMode.DST_OUT);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint);
+ }
+ }
+
+ /**
+ * Shifts up the main window.
+ * @hide
+ */
+ public static final class ShiftUpAnimation {
+ private final float mFromYDelta;
+ private final float mToYDelta;
+ private final View mOccludeHoleView;
+ private final SyncRtSurfaceTransactionApplier mApplier;
+ private final Matrix mTmpTransform = new Matrix();
+ private final SurfaceControl mFirstWindowSurface;
+ private final ViewGroup mSplashScreenView;
+ private final TransactionPool mTransactionPool;
+ private final Rect mFirstWindowFrame;
+ private final int mMainWindowShiftLength;
+
+ public ShiftUpAnimation(float fromYDelta, float toYDelta, View occludeHoleView,
+ SurfaceControl firstWindowSurface, ViewGroup splashScreenView,
+ TransactionPool transactionPool, Rect firstWindowFrame,
+ int mainWindowShiftLength) {
+ mFromYDelta = fromYDelta;
+ mToYDelta = toYDelta;
+ mOccludeHoleView = occludeHoleView;
+ mApplier = new SyncRtSurfaceTransactionApplier(occludeHoleView);
+ mFirstWindowSurface = firstWindowSurface;
+ mSplashScreenView = splashScreenView;
+ mTransactionPool = transactionPool;
+ mFirstWindowFrame = firstWindowFrame;
+ mMainWindowShiftLength = mainWindowShiftLength;
+ }
+
+ void onAnimationProgress(float linearProgress) {
+ if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()
+ || !mSplashScreenView.isAttachedToWindow()) {
+ return;
+ }
+
+ final float progress = SHIFT_UP_INTERPOLATOR.getInterpolation(linearProgress);
+ final float dy = mFromYDelta + (mToYDelta - mFromYDelta) * progress;
+
+ mOccludeHoleView.setTranslationY(dy);
+ mTmpTransform.setTranslate(0 /* dx */, dy);
+
+ // set the vsyncId to ensure the transaction doesn't get applied too early.
+ final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+ tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+ mTmpTransform.postTranslate(mFirstWindowFrame.left,
+ mFirstWindowFrame.top + mMainWindowShiftLength);
+
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ params = new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(mFirstWindowSurface)
+ .withMatrix(mTmpTransform)
+ .withMergeTransaction(tx)
+ .build();
+ mApplier.scheduleApply(params);
+
+ mTransactionPool.release(tx);
+ }
+
+ void finish() {
+ if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
+ return;
+ }
+ final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+ if (mSplashScreenView.isAttachedToWindow()) {
+ tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ params = new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(mFirstWindowSurface)
+ .withWindowCrop(null)
+ .withMergeTransaction(tx)
+ .build();
+ mApplier.scheduleApply(params);
+ } else {
+ tx.setWindowCrop(mFirstWindowSurface, null);
+ tx.apply();
+ }
+ mTransactionPool.release(tx);
+
+ Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT,
+ mFirstWindowSurface::release, null);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 3df33f346e84..dca516a327b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -19,15 +19,20 @@ package com.android.wm.shell.windowdecor;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
+import android.hardware.input.InputManager;
import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Log;
import android.util.SparseArray;
import android.view.Choreographer;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
@@ -47,7 +52,9 @@ import com.android.wm.shell.transition.Transitions;
* View model for the window decoration with a caption and shadows. Works with
* {@link CaptionWindowDecoration}.
*/
+
public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
+ private static final String TAG = "CaptionViewModel";
private final ActivityTaskManager mActivityTaskManager;
private final ShellTaskOrganizer mTaskOrganizer;
private final Context mContext;
@@ -107,7 +114,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
windowDecoration.setDragResizeCallback(taskPositioner);
setupWindowDecorationForTransition(taskInfo, startT, finishT);
- setupCaptionColor(taskInfo, windowDecoration);
return true;
}
@@ -117,12 +123,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
if (decoration == null) return;
decoration.relayout(taskInfo);
- setupCaptionColor(taskInfo, decoration);
- }
-
- private void setupCaptionColor(RunningTaskInfo taskInfo, CaptionWindowDecoration decoration) {
- int statusBarColor = taskInfo.taskDescription.getStatusBarColor();
- decoration.setCaptionColor(statusBarColor);
}
@Override
@@ -153,6 +153,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private final DragResizeCallback mDragResizeCallback;
private int mDragPointerId = -1;
+ private boolean mDragActive = false;
private CaptionTouchEventListener(
RunningTaskInfo taskInfo,
@@ -173,42 +174,38 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
} else {
mSyncQueue.queue(wct);
}
- } else if (id == R.id.maximize_window) {
- WindowContainerTransaction wct = new WindowContainerTransaction();
- RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
- int targetWindowingMode = taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
- ? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_FREEFORM;
- int displayWindowingMode =
- taskInfo.configuration.windowConfiguration.getDisplayWindowingMode();
- wct.setWindowingMode(mTaskToken,
- targetWindowingMode == displayWindowingMode
- ? WINDOWING_MODE_UNDEFINED : targetWindowingMode);
- if (targetWindowingMode == WINDOWING_MODE_FULLSCREEN) {
- wct.setBounds(mTaskToken, null);
- }
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mTransitionStarter.startWindowingModeTransition(targetWindowingMode, wct);
- } else {
- mSyncQueue.queue(wct);
- }
- } else if (id == R.id.minimize_window) {
- WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reorder(mTaskToken, false);
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mTransitionStarter.startMinimizedModeTransition(wct);
- } else {
- mSyncQueue.queue(wct);
- }
+ } else if (id == R.id.back_button) {
+ injectBackKey();
+ }
+ }
+ private void injectBackKey() {
+ sendBackEvent(KeyEvent.ACTION_DOWN);
+ sendBackEvent(KeyEvent.ACTION_UP);
+ }
+
+ private void sendBackEvent(int action) {
+ final long when = SystemClock.uptimeMillis();
+ final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK,
+ 0 /* repeat */, 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD,
+ 0 /* scancode */, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
+ InputDevice.SOURCE_KEYBOARD);
+
+ ev.setDisplayId(mContext.getDisplay().getDisplayId());
+ if (!InputManager.getInstance()
+ .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC)) {
+ Log.e(TAG, "Inject input event fail");
}
}
@Override
public boolean onTouch(View v, MotionEvent e) {
- if (v.getId() != R.id.caption) {
+ int id = v.getId();
+ if (id != R.id.caption_handle && id != R.id.caption) {
return false;
}
- handleEventForMove(e);
-
+ if (id == R.id.caption_handle || mDragActive) {
+ handleEventForMove(e);
+ }
if (e.getAction() != MotionEvent.ACTION_DOWN) {
return false;
}
@@ -231,6 +228,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
+ mDragActive = true;
mDragPointerId = e.getPointerId(0);
mDragResizeCallback.onDragResizeStart(
0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
@@ -243,6 +241,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
+ mDragActive = false;
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
int statusBarHeight = mDisplayController.getDisplayLayout(taskInfo.displayId)
.stableInsets().top;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 733f6b7d5dbf..beace75d9fca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -22,12 +22,12 @@ import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.VectorDrawable;
import android.os.Handler;
import android.view.Choreographer;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.ViewConfiguration;
import android.window.WindowContainerTransaction;
import com.android.wm.shell.R;
@@ -38,11 +38,7 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus;
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
- * {@link CaptionWindowDecorViewModel}. The caption bar contains maximize and close buttons.
- *
- * {@link CaptionWindowDecorViewModel} can change the color of the caption bar based on the foremost
- * app's request through {@link #setCaptionColor(int)}, in which it changes the foreground color of
- * caption buttons according to the luminance of the background.
+ * {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close button.
*
* The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
*/
@@ -54,7 +50,10 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
// Height of button (32dp) + 2 * margin (5dp each)
private static final int DECOR_CAPTION_HEIGHT_IN_DIP = 42;
+ // Width of buttons (64dp) + handle (128dp) + padding (24dp total)
+ private static final int DECOR_CAPTION_WIDTH_IN_DIP = 216;
private static final int RESIZE_HANDLE_IN_DIP = 30;
+ private static final int RESIZE_CORNER_IN_DIP = 44;
private static final Rect EMPTY_OUTSET = new Rect();
private static final Rect RESIZE_HANDLE_OUTSET = new Rect(
@@ -73,6 +72,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
new WindowDecoration.RelayoutResult<>();
+ private boolean mDesktopActive;
+
CaptionWindowDecoration(
Context context,
DisplayController displayController,
@@ -87,6 +88,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
mHandler = handler;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
+ mDesktopActive = DesktopModeStatus.isActive(mContext);
}
void setCaptionListeners(
@@ -123,8 +125,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
final WindowContainerTransaction wct = new WindowContainerTransaction();
relayout(taskInfo, R.layout.caption_window_decoration, oldRootView,
- DECOR_CAPTION_HEIGHT_IN_DIP, outset, shadowRadiusDp, startT, finishT, wct, mResult);
- taskInfo = null; // Clear it just in case we use it accidentally
+ DECOR_CAPTION_HEIGHT_IN_DIP, DECOR_CAPTION_WIDTH_IN_DIP, outset, shadowRadiusDp,
+ startT, finishT, wct, mResult);
mTaskOrganizer.applyTransaction(wct);
@@ -137,6 +139,17 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
setupRootView();
}
+ // If this task is not focused, do not show caption.
+ setCaptionVisibility(taskInfo.isFocused);
+
+ // Only handle should show if Desktop Mode is inactive.
+ boolean desktopCurrentStatus = DesktopModeStatus.isActive(mContext);
+ if (mDesktopActive != desktopCurrentStatus && taskInfo.isFocused) {
+ mDesktopActive = desktopCurrentStatus;
+ setButtonVisibility();
+ }
+ taskInfo = null; // Clear it just in case we use it accidentally
+
if (!isDragResizeable) {
closeDragResizeListener();
return;
@@ -145,16 +158,19 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
if (oldDecorationSurface != mDecorationContainerSurface || mDragResizeListener == null) {
closeDragResizeListener();
mDragResizeListener = new DragResizeInputListener(
- mContext,
- mHandler,
- mChoreographer,
- mDisplay.getDisplayId(),
- mDecorationContainerSurface,
- mDragResizeCallback);
+ mContext,
+ mHandler,
+ mChoreographer,
+ mDisplay.getDisplayId(),
+ mDecorationContainerSurface,
+ mDragResizeCallback);
}
+ int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext()).getScaledTouchSlop();
+
mDragResizeListener.setGeometry(
- mResult.mWidth, mResult.mHeight, (int) (mResult.mDensity * RESIZE_HANDLE_IN_DIP));
+ mResult.mWidth, mResult.mHeight, (int) (mResult.mDensity * RESIZE_HANDLE_IN_DIP),
+ (int) (mResult.mDensity * RESIZE_CORNER_IN_DIP), touchSlop);
}
/**
@@ -163,42 +179,46 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
private void setupRootView() {
View caption = mResult.mRootView.findViewById(R.id.caption);
caption.setOnTouchListener(mOnCaptionTouchListener);
- View maximize = caption.findViewById(R.id.maximize_window);
- if (DesktopModeStatus.IS_SUPPORTED) {
- // Hide maximize button when desktop mode is available
- maximize.setVisibility(View.GONE);
- } else {
- maximize.setVisibility(View.VISIBLE);
- maximize.setOnClickListener(mOnCaptionButtonClickListener);
- }
View close = caption.findViewById(R.id.close_window);
close.setOnClickListener(mOnCaptionButtonClickListener);
- View minimize = caption.findViewById(R.id.minimize_window);
- minimize.setOnClickListener(mOnCaptionButtonClickListener);
+ View back = caption.findViewById(R.id.back_button);
+ back.setOnClickListener(mOnCaptionButtonClickListener);
+ View handle = caption.findViewById(R.id.caption_handle);
+ handle.setOnTouchListener(mOnCaptionTouchListener);
+ setButtonVisibility();
}
- void setCaptionColor(int captionColor) {
- if (mResult.mRootView == null) {
- return;
- }
-
+ /**
+ * Sets caption visibility based on task focus.
+ *
+ * @param visible whether or not the caption should be visible
+ */
+ private void setCaptionVisibility(boolean visible) {
+ int v = visible ? View.VISIBLE : View.GONE;
View caption = mResult.mRootView.findViewById(R.id.caption);
- GradientDrawable captionDrawable = (GradientDrawable) caption.getBackground();
- captionDrawable.setColor(captionColor);
+ caption.setVisibility(v);
+ }
+ /**
+ * Sets the visibility of buttons and color of caption based on desktop mode status
+ *
+ */
+ public void setButtonVisibility() {
+ int v = mDesktopActive ? View.VISIBLE : View.GONE;
+ View caption = mResult.mRootView.findViewById(R.id.caption);
+ View back = caption.findViewById(R.id.back_button);
+ View close = caption.findViewById(R.id.close_window);
+ back.setVisibility(v);
+ close.setVisibility(v);
int buttonTintColorRes =
- Color.valueOf(captionColor).luminance() < 0.5
- ? R.color.decor_button_light_color
- : R.color.decor_button_dark_color;
+ mDesktopActive ? R.color.decor_button_dark_color
+ : R.color.decor_button_light_color;
ColorStateList buttonTintColor =
caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
- View maximize = caption.findViewById(R.id.maximize_window);
- VectorDrawable maximizeBackground = (VectorDrawable) maximize.getBackground();
- maximizeBackground.setTintList(buttonTintColor);
-
- View close = caption.findViewById(R.id.close_window);
- VectorDrawable closeBackground = (VectorDrawable) close.getBackground();
- closeBackground.setTintList(buttonTintColor);
+ View handle = caption.findViewById(R.id.caption_handle);
+ VectorDrawable handleBackground = (VectorDrawable) handle.getBackground();
+ handleBackground.setTintList(buttonTintColor);
+ caption.setBackgroundColor(v == View.VISIBLE ? Color.WHITE : Color.TRANSPARENT);
}
private void closeDragResizeListener() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 3d014959a952..b9f16b63de48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -16,11 +16,13 @@
package com.android.wm.shell.windowdecor;
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import android.content.Context;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
@@ -42,8 +44,11 @@ import com.android.internal.view.BaseIWindow;
/**
* An input event listener registered to InputDispatcher to receive input events on task edges and
- * convert them to drag resize requests.
+ * and corners. Converts them to drag resize requests.
+ * Task edges are for resizing with a mouse.
+ * Task corners are for resizing with touch input.
*/
+// TODO(b/251270585): investigate how to pass taps in corners to the tasks
class DragResizeInputListener implements AutoCloseable {
private static final String TAG = "DragResizeInputListener";
@@ -63,8 +68,15 @@ class DragResizeInputListener implements AutoCloseable {
private int mWidth;
private int mHeight;
private int mResizeHandleThickness;
+ private int mCornerSize;
+
+ private Rect mLeftTopCornerBounds;
+ private Rect mRightTopCornerBounds;
+ private Rect mLeftBottomCornerBounds;
+ private Rect mRightBottomCornerBounds;
private int mDragPointerId = -1;
+ private int mTouchSlop;
DragResizeInputListener(
Context context,
@@ -118,16 +130,23 @@ class DragResizeInputListener implements AutoCloseable {
* @param height The height of the drag resize handler in pixels, including resize handle
* thickness. That is task height + 2 * resize handle thickness.
* @param resizeHandleThickness The thickness of the resize handle in pixels.
+ * @param cornerSize The size of the resize handle centered in each corner.
+ * @param touchSlop The distance in pixels user has to drag with touch for it to register as
+ * a resize action.
*/
- void setGeometry(int width, int height, int resizeHandleThickness) {
+ void setGeometry(int width, int height, int resizeHandleThickness, int cornerSize,
+ int touchSlop) {
if (mWidth == width && mHeight == height
- && mResizeHandleThickness == resizeHandleThickness) {
+ && mResizeHandleThickness == resizeHandleThickness
+ && mCornerSize == cornerSize) {
return;
}
mWidth = width;
mHeight = height;
mResizeHandleThickness = resizeHandleThickness;
+ mCornerSize = cornerSize;
+ mTouchSlop = touchSlop;
Region touchRegion = new Region();
final Rect topInputBounds = new Rect(0, 0, mWidth, mResizeHandleThickness);
@@ -146,6 +165,40 @@ class DragResizeInputListener implements AutoCloseable {
mWidth, mHeight);
touchRegion.union(bottomInputBounds);
+ // Set up touch areas in each corner.
+ int cornerRadius = mCornerSize / 2;
+ mLeftTopCornerBounds = new Rect(
+ mResizeHandleThickness - cornerRadius,
+ mResizeHandleThickness - cornerRadius,
+ mResizeHandleThickness + cornerRadius,
+ mResizeHandleThickness + cornerRadius
+ );
+ touchRegion.union(mLeftTopCornerBounds);
+
+ mRightTopCornerBounds = new Rect(
+ mWidth - mResizeHandleThickness - cornerRadius,
+ mResizeHandleThickness - cornerRadius,
+ mWidth - mResizeHandleThickness + cornerRadius,
+ mResizeHandleThickness + cornerRadius
+ );
+ touchRegion.union(mRightTopCornerBounds);
+
+ mLeftBottomCornerBounds = new Rect(
+ mResizeHandleThickness - cornerRadius,
+ mHeight - mResizeHandleThickness - cornerRadius,
+ mResizeHandleThickness + cornerRadius,
+ mHeight - mResizeHandleThickness + cornerRadius
+ );
+ touchRegion.union(mLeftBottomCornerBounds);
+
+ mRightBottomCornerBounds = new Rect(
+ mWidth - mResizeHandleThickness - cornerRadius,
+ mHeight - mResizeHandleThickness - cornerRadius,
+ mWidth - mResizeHandleThickness + cornerRadius,
+ mHeight - mResizeHandleThickness + cornerRadius
+ );
+ touchRegion.union(mRightBottomCornerBounds);
+
try {
mWindowSession.updateInputChannel(
mInputChannel.getToken(),
@@ -173,6 +226,9 @@ class DragResizeInputListener implements AutoCloseable {
private final Choreographer mChoreographer;
private final Runnable mConsumeBatchEventRunnable;
private boolean mConsumeBatchEventScheduled;
+ private boolean mShouldHandleEvents;
+ private boolean mDragging;
+ private final PointF mActionDownPoint = new PointF();
private TaskResizeInputEventReceiver(
InputChannel inputChannel, Handler handler, Choreographer choreographer) {
@@ -216,41 +272,101 @@ class DragResizeInputListener implements AutoCloseable {
}
MotionEvent e = (MotionEvent) inputEvent;
+ boolean result = false;
+ // Check if this is a touch event vs mouse event.
+ // Touch events are tracked in four corners. Other events are tracked in resize edges.
+ boolean isTouch = (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
+
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
- mDragPointerId = e.getPointerId(0);
- mCallback.onDragResizeStart(
- calculateCtrlType(e.getX(0), e.getY(0)), e.getRawX(0), e.getRawY(0));
+ float x = e.getX(0);
+ float y = e.getY(0);
+ if (isTouch) {
+ mShouldHandleEvents = isInCornerBounds(x, y);
+ } else {
+ mShouldHandleEvents = isInResizeHandleBounds(x, y);
+ }
+ if (mShouldHandleEvents) {
+ mDragPointerId = e.getPointerId(0);
+ float rawX = e.getRawX(0);
+ float rawY = e.getRawY(0);
+ mActionDownPoint.set(rawX, rawY);
+ int ctrlType = calculateCtrlType(isTouch, x, y);
+ mCallback.onDragResizeStart(ctrlType, rawX, rawY);
+ result = true;
+ }
break;
}
case MotionEvent.ACTION_MOVE: {
+ if (!mShouldHandleEvents) {
+ break;
+ }
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
- mCallback.onDragResizeMove(
- e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
+ float rawX = e.getRawX(dragPointerIndex);
+ float rawY = e.getRawY(dragPointerIndex);
+ if (isTouch) {
+ // Check for touch slop for touch events
+ float dx = rawX - mActionDownPoint.x;
+ float dy = rawY - mActionDownPoint.y;
+ if (!mDragging && Math.hypot(dx, dy) > mTouchSlop) {
+ mDragging = true;
+ }
+ } else {
+ // For all other types allow immediate dragging.
+ mDragging = true;
+ }
+ if (mDragging) {
+ mCallback.onDragResizeMove(rawX, rawY);
+ result = true;
+ }
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
- int dragPointerIndex = e.findPointerIndex(mDragPointerId);
- mCallback.onDragResizeEnd(
- e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
+ if (mDragging) {
+ int dragPointerIndex = e.findPointerIndex(mDragPointerId);
+ mCallback.onDragResizeEnd(
+ e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
+ }
+ mDragging = false;
+ mShouldHandleEvents = false;
+ mActionDownPoint.set(0, 0);
mDragPointerId = -1;
+ result = true;
break;
}
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE: {
updateCursorType(e.getXCursorPosition(), e.getYCursorPosition());
+ result = true;
break;
}
case MotionEvent.ACTION_HOVER_EXIT:
mInputManager.setPointerIconType(PointerIcon.TYPE_DEFAULT);
+ result = true;
break;
}
- return true;
+ return result;
+ }
+
+ private boolean isInCornerBounds(float xf, float yf) {
+ return calculateCornersCtrlType(xf, yf) != 0;
+ }
+
+ private boolean isInResizeHandleBounds(float x, float y) {
+ return calculateResizeHandlesCtrlType(x, y) != 0;
+ }
+
+ @TaskPositioner.CtrlType
+ private int calculateCtrlType(boolean isTouch, float x, float y) {
+ if (isTouch) {
+ return calculateCornersCtrlType(x, y);
+ }
+ return calculateResizeHandlesCtrlType(x, y);
}
@TaskPositioner.CtrlType
- private int calculateCtrlType(float x, float y) {
+ private int calculateResizeHandlesCtrlType(float x, float y) {
int ctrlType = 0;
if (x < mResizeHandleThickness) {
ctrlType |= TaskPositioner.CTRL_TYPE_LEFT;
@@ -267,8 +383,27 @@ class DragResizeInputListener implements AutoCloseable {
return ctrlType;
}
+ @TaskPositioner.CtrlType
+ private int calculateCornersCtrlType(float x, float y) {
+ int xi = (int) x;
+ int yi = (int) y;
+ if (mLeftTopCornerBounds.contains(xi, yi)) {
+ return TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_TOP;
+ }
+ if (mLeftBottomCornerBounds.contains(xi, yi)) {
+ return TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_BOTTOM;
+ }
+ if (mRightTopCornerBounds.contains(xi, yi)) {
+ return TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_TOP;
+ }
+ if (mRightBottomCornerBounds.contains(xi, yi)) {
+ return TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_BOTTOM;
+ }
+ return 0;
+ }
+
private void updateCursorType(float x, float y) {
- @TaskPositioner.CtrlType int ctrlType = calculateCtrlType(x, y);
+ @TaskPositioner.CtrlType int ctrlType = calculateResizeHandlesCtrlType(x, y);
int cursorType = PointerIcon.TYPE_DEFAULT;
switch (ctrlType) {
@@ -292,4 +427,4 @@ class DragResizeInputListener implements AutoCloseable {
mInputManager.setPointerIconType(cursorType);
}
}
-}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index 280569b05d87..27c10114ac0e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -25,9 +25,10 @@ import com.android.wm.shell.ShellTaskOrganizer;
class TaskPositioner implements DragResizeCallback {
- @IntDef({CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM})
+ @IntDef({CTRL_TYPE_UNDEFINED, CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM})
@interface CtrlType {}
+ static final int CTRL_TYPE_UNDEFINED = 0;
static final int CTRL_TYPE_LEFT = 1;
static final int CTRL_TYPE_RIGHT = 2;
static final int CTRL_TYPE_TOP = 4;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 3e3a864f48c7..bf863ea2c7ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -143,9 +143,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
abstract void relayout(RunningTaskInfo taskInfo);
void relayout(RunningTaskInfo taskInfo, int layoutResId, T rootView, float captionHeightDp,
- Rect outsetsDp, float shadowRadiusDp, SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT, WindowContainerTransaction wct,
- RelayoutResult<T> outResult) {
+ float captionWidthDp, Rect outsetsDp, float shadowRadiusDp,
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+ WindowContainerTransaction wct, RelayoutResult<T> outResult) {
outResult.reset();
final Configuration oldTaskConfig = mTaskInfo.getConfiguration();
@@ -249,8 +249,15 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
final int captionHeight = (int) Math.ceil(captionHeightDp * outResult.mDensity);
+ final int captionWidth = (int) Math.ceil(captionWidthDp * outResult.mDensity);
+
+ //Prevent caption from going offscreen if task is too high up
+ final int captionYPos = taskBounds.top <= captionHeight / 2 ? 0 : captionHeight / 2;
+
startT.setPosition(
- mCaptionContainerSurface, -decorContainerOffsetX, -decorContainerOffsetY)
+ mCaptionContainerSurface, -decorContainerOffsetX
+ + taskBounds.width() / 2 - captionWidth / 2,
+ -decorContainerOffsetY - captionYPos)
.setWindowCrop(mCaptionContainerSurface, taskBounds.width(), captionHeight)
.show(mCaptionContainerSurface);
@@ -264,7 +271,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
// Caption view
mCaptionWindowManager.setConfiguration(taskConfig);
final WindowManager.LayoutParams lp =
- new WindowManager.LayoutParams(taskBounds.width(), captionHeight,
+ new WindowManager.LayoutParams(captionWidth, captionHeight,
WindowManager.LayoutParams.TYPE_APPLICATION,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
@@ -282,7 +289,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
// Caption insets
mCaptionInsetsRect.set(taskBounds);
- mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + captionHeight;
+ mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + captionHeight - captionYPos;
wct.addRectInsetsProvider(mTaskInfo.token, mCaptionInsetsRect, CAPTION_INSETS_TYPES);
} else {
startT.hide(mCaptionContainerSurface);
@@ -388,4 +395,4 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
return new SurfaceControlViewHost(c, d, wmm);
}
}
-}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index 3d8525b3d4ad..1dc03b9b8900 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -18,12 +18,12 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.Test
/** Base class for pip expand tests */
abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
- protected val testApp = FixedOrientationAppHelper(instrumentation)
+ protected val testApp = SimpleAppHelper(instrumentation)
/**
* Checks that the pip app window remains inside the display bounds throughout the whole
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index 201594bca8e3..936afa9801b7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -171,7 +171,7 @@ class EnterSplitScreenFromOverview(testSpec: FlickerTestParameter) : SplitScreen
super.taskBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest(bugId = 252736515)
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index e2f7f7e583be..1f117d0cbd94 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -151,7 +151,7 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas
override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest(bugId = 252736515)
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 695550dd8fa5..f6d6c03bc2ee 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.common.split;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.google.common.truth.Truth.assertThat;
@@ -91,6 +92,14 @@ public class SplitLayoutTests extends ShellTestCase {
// Verify updateConfiguration returns true if the root bounds changed.
config.windowConfiguration.setBounds(new Rect(0, 0, 2160, 1080));
assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
+
+ // Verify updateConfiguration returns true if the orientation changed.
+ config.orientation = ORIENTATION_LANDSCAPE;
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
+
+ // Verify updateConfiguration returns true if the density changed.
+ config.densityDpi = 123;
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index ab6ac949d4a3..fa62b9c00fc7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -77,6 +77,7 @@ import java.util.function.Supplier;
@RunWith(AndroidTestingRunner.class)
public class WindowDecorationTests extends ShellTestCase {
private static final int CAPTION_HEIGHT_DP = 32;
+ private static final int CAPTION_WIDTH_DP = 216;
private static final int SHADOW_RADIUS_DP = 5;
private static final Rect TASK_BOUNDS = new Rect(100, 300, 400, 400);
private static final Point TASK_POSITION_IN_PARENT = new Point(40, 60);
@@ -220,7 +221,7 @@ public class WindowDecorationTests extends ShellTestCase {
verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
verify(captionContainerSurfaceBuilder).setContainerLayer();
- verify(mMockSurfaceControlStartT).setPosition(captionContainerSurface, 20, 40);
+ verify(mMockSurfaceControlStartT).setPosition(captionContainerSurface, -46, 8);
verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
verify(mMockSurfaceControlStartT).show(captionContainerSurface);
@@ -410,7 +411,7 @@ public class WindowDecorationTests extends ShellTestCase {
@Override
void relayout(ActivityManager.RunningTaskInfo taskInfo) {
relayout(null /* taskInfo */, 0 /* layoutResId */, mMockView, CAPTION_HEIGHT_DP,
- mOutsetsDp, SHADOW_RADIUS_DP, mMockSurfaceControlStartT,
+ CAPTION_WIDTH_DP, mOutsetsDp, SHADOW_RADIUS_DP, mMockSurfaceControlStartT,
mMockSurfaceControlFinishT, mMockWindowContainerTransaction, mRelayoutResult);
}
}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 235700b27c25..1381bdd6a50d 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -1068,7 +1068,7 @@ base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::ResolveBag(
base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(uint32_t resid) const {
std::vector<uint32_t> found_resids;
const auto bag = GetBag(resid, found_resids);
- cached_bag_resid_stacks_.emplace(resid, found_resids);
+ cached_bag_resid_stacks_.emplace(resid, std::move(found_resids));
return bag;
}
@@ -1468,7 +1468,6 @@ base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid,
continue;
}
- Theme::Entry new_entry{attr_res_id, it->cookie, (*bag)->type_spec_flags, it->value};
auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), attr_res_id,
ThemeEntryKeyComparer{});
if (entry_it != entries_.end() && entry_it->attr_res_id == attr_res_id) {
@@ -1477,10 +1476,10 @@ base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid,
/// true.
entries_.erase(entry_it);
} else if (force) {
- *entry_it = new_entry;
+ *entry_it = Entry{attr_res_id, it->cookie, (*bag)->type_spec_flags, it->value};
}
} else {
- entries_.insert(entry_it, new_entry);
+ entries_.insert(entry_it, Entry{attr_res_id, it->cookie, (*bag)->type_spec_flags, it->value});
}
}
return {};
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 5e8a623d4205..46fbe7c4d981 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1020,6 +1020,40 @@ base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> ResStringPool:
return base::unexpected(std::nullopt);
}
+template <typename TChar, typename SP>
+base::expected<size_t, NullOrIOError> ResStringPool::stringIndex(
+ SP sp, std::unordered_map<SP, size_t>& map) const
+{
+ AutoMutex lock(mStringIndexLock);
+
+ if (map.empty()) {
+ // build string index on the first call
+ for (size_t i = 0; i < mHeader->stringCount; i++) {
+ base::expected<SP, NullOrIOError> s;
+ if constexpr(std::is_same_v<TChar, char16_t>) {
+ s = stringAt(i);
+ } else {
+ s = string8At(i);
+ }
+ if (s.has_value()) {
+ const auto r = map.insert({*s, i});
+ if (!r.second) {
+ ALOGE("failed to build string index, string id=%zu\n", i);
+ }
+ } else {
+ return base::unexpected(s.error());
+ }
+ }
+ }
+
+ if (!map.empty()) {
+ const auto result = map.find(sp);
+ if (result != map.end())
+ return result->second;
+ }
+ return base::unexpected(std::nullopt);
+}
+
base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_t* str,
size_t strLen) const
{
@@ -1027,134 +1061,28 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_
return base::unexpected(std::nullopt);
}
- if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) {
- if (kDebugStringPoolNoisy) {
- ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string());
- }
-
- // The string pool contains UTF 8 strings; we don't want to cause
- // temporary UTF-16 strings to be created as we search.
- if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
- // Do a binary search for the string... this is a little tricky,
- // because the strings are sorted with strzcmp16(). So to match
- // the ordering, we need to convert strings in the pool to UTF-16.
- // But we don't want to hit the cache, so instead we will have a
- // local temporary allocation for the conversions.
- size_t convBufferLen = strLen + 4;
- std::vector<char16_t> convBuffer(convBufferLen);
- ssize_t l = 0;
- ssize_t h = mHeader->stringCount-1;
-
- ssize_t mid;
- while (l <= h) {
- mid = l + (h - l)/2;
- int c = -1;
- const base::expected<StringPiece, NullOrIOError> s = string8At(mid);
- if (UNLIKELY(IsIOError(s))) {
- return base::unexpected(s.error());
- }
- if (s.has_value()) {
- char16_t* end = utf8_to_utf16(reinterpret_cast<const uint8_t*>(s->data()),
- s->size(), convBuffer.data(), convBufferLen);
- c = strzcmp16(convBuffer.data(), end-convBuffer.data(), str, strLen);
- }
- if (kDebugStringPoolNoisy) {
- ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
- s->data(), c, (int)l, (int)mid, (int)h);
- }
- if (c == 0) {
- if (kDebugStringPoolNoisy) {
- ALOGI("MATCH!");
- }
- return mid;
- } else if (c < 0) {
- l = mid + 1;
- } else {
- h = mid - 1;
- }
- }
- } else {
- // It is unusual to get the ID from an unsorted string block...
- // most often this happens because we want to get IDs for style
- // span tags; since those always appear at the end of the string
- // block, start searching at the back.
- String8 str8(str, strLen);
- const size_t str8Len = str8.size();
- for (int i=mHeader->stringCount-1; i>=0; i--) {
- const base::expected<StringPiece, NullOrIOError> s = string8At(i);
- if (UNLIKELY(IsIOError(s))) {
- return base::unexpected(s.error());
- }
- if (s.has_value()) {
- if (kDebugStringPoolNoisy) {
- ALOGI("Looking at %s, i=%d\n", s->data(), i);
- }
- if (str8Len == s->size()
- && memcmp(s->data(), str8.string(), str8Len) == 0) {
- if (kDebugStringPoolNoisy) {
- ALOGI("MATCH!");
- }
- return i;
- }
- }
- }
- }
+ if (kDebugStringPoolNoisy) {
+ ALOGI("indexOfString (%s): %s", isUTF8() ? "UTF-8" : "UTF-16",
+ String8(str, strLen).string());
+ }
+ base::expected<size_t, NullOrIOError> idx;
+ if (isUTF8()) {
+ auto str8 = String8(str, strLen);
+ idx = stringIndex<char>(StringPiece(str8.c_str(), str8.size()), mStringIndex8);
} else {
- if (kDebugStringPoolNoisy) {
- ALOGI("indexOfString UTF-16: %s", String8(str, strLen).string());
- }
+ idx = stringIndex<char16_t>(StringPiece16(str, strLen), mStringIndex16);
+ }
- if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
- // Do a binary search for the string...
- ssize_t l = 0;
- ssize_t h = mHeader->stringCount-1;
+ if (UNLIKELY(!idx.has_value())) {
+ return base::unexpected(idx.error());
+ }
- ssize_t mid;
- while (l <= h) {
- mid = l + (h - l)/2;
- const base::expected<StringPiece16, NullOrIOError> s = stringAt(mid);
- if (UNLIKELY(IsIOError(s))) {
- return base::unexpected(s.error());
- }
- int c = s.has_value() ? strzcmp16(s->data(), s->size(), str, strLen) : -1;
- if (kDebugStringPoolNoisy) {
- ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
- String8(s->data(), s->size()).string(), c, (int)l, (int)mid, (int)h);
- }
- if (c == 0) {
- if (kDebugStringPoolNoisy) {
- ALOGI("MATCH!");
- }
- return mid;
- } else if (c < 0) {
- l = mid + 1;
- } else {
- h = mid - 1;
- }
- }
- } else {
- // It is unusual to get the ID from an unsorted string block...
- // most often this happens because we want to get IDs for style
- // span tags; since those always appear at the end of the string
- // block, start searching at the back.
- for (int i=mHeader->stringCount-1; i>=0; i--) {
- const base::expected<StringPiece16, NullOrIOError> s = stringAt(i);
- if (UNLIKELY(IsIOError(s))) {
- return base::unexpected(s.error());
- }
- if (kDebugStringPoolNoisy) {
- ALOGI("Looking at %s, i=%d\n", String8(s->data(), s->size()).string(), i);
- }
- if (s.has_value() && strLen == s->size() &&
- strzcmp16(s->data(), s->size(), str, strLen) == 0) {
- if (kDebugStringPoolNoisy) {
- ALOGI("MATCH!");
- }
- return i;
- }
- }
+ if (*idx < mHeader->stringCount) {
+ if (kDebugStringPoolNoisy) {
+ ALOGI("MATCH! (idx=%zu)", *idx);
}
+ return *idx;
}
return base::unexpected(std::nullopt);
}
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 9309091f4124..24628cd36ba5 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -41,6 +41,7 @@
#include <array>
#include <map>
#include <memory>
+#include <unordered_map>
namespace android {
@@ -562,8 +563,17 @@ private:
incfs::map_ptr<uint32_t> mStyles;
uint32_t mStylePoolSize; // number of uint32_t
+ // mStringIndex is used to quickly map a string to its ID
+ mutable Mutex mStringIndexLock;
+ mutable std::unordered_map<StringPiece, size_t> mStringIndex8;
+ mutable std::unordered_map<StringPiece16, size_t> mStringIndex16;
+
base::expected<StringPiece, NullOrIOError> stringDecodeAt(
size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const;
+
+ template <typename TChar, typename SP=BasicStringPiece<TChar>>
+ base::expected<size_t, NullOrIOError> stringIndex(
+ SP str, std::unordered_map<SP, size_t>& map) const;
};
/**
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index a5c0924579eb..b763a96e8e8a 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -169,6 +169,8 @@ void DeferredLayerUpdater::apply() {
sk_sp<SkImage> layerImage = mImageSlots[slot].createIfNeeded(
hardwareBuffer, dataspace, newContent,
mRenderState.getRenderThread().getGrContext());
+ AHardwareBuffer_Desc bufferDesc;
+ AHardwareBuffer_describe(hardwareBuffer, &bufferDesc);
// unref to match the ref added by ASurfaceTexture_dequeueBuffer. eglCreateImageKHR
// (invoked by createIfNeeded) will add a ref to the AHardwareBuffer.
AHardwareBuffer_release(hardwareBuffer);
@@ -189,6 +191,7 @@ void DeferredLayerUpdater::apply() {
maxLuminanceNits =
std::max(cta861_3.maxContentLightLevel, maxLuminanceNits);
}
+ mLayer->setBufferFormat(bufferDesc.format);
updateLayer(forceFilter, layerImage, outTransform, currentCropRect,
maxLuminanceNits);
}
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 47eb5d3bfb83..345749b6d920 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -102,6 +102,10 @@ public:
inline float getMaxLuminanceNits() { return mMaxLuminanceNits; }
+ void setBufferFormat(uint32_t format) { mBufferFormat = format; }
+
+ uint32_t getBufferFormat() const { return mBufferFormat; }
+
void draw(SkCanvas* canvas);
protected:
@@ -169,6 +173,8 @@ private:
*/
float mMaxLuminanceNits = -1;
+ uint32_t mBufferFormat = 0;
+
}; // struct Layer
} // namespace uirenderer
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 24443c8c9836..7170226037f9 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -71,9 +71,14 @@ public:
, right(rect.fRight)
, bottom(rect.fBottom) {}
- friend int operator==(const Rect& a, const Rect& b) { return !memcmp(&a, &b, sizeof(a)); }
+ friend int operator==(const Rect& a, const Rect& b) {
+ return a.left == b.left &&
+ a.top == b.top &&
+ a.right == b.right &&
+ a.bottom == b.bottom;
+ }
- friend int operator!=(const Rect& a, const Rect& b) { return memcmp(&a, &b, sizeof(a)); }
+ friend int operator!=(const Rect& a, const Rect& b) { return !(a == b); }
inline void clear() { left = top = right = bottom = 0.0f; }
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 2fba13c3cfea..3ba540921f64 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -107,6 +107,32 @@ static bool isHdrDataspace(ui::Dataspace dataspace) {
return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
}
+static void adjustCropForYUV(uint32_t format, int bufferWidth, int bufferHeight, SkRect* cropRect) {
+ // Chroma channels of YUV420 images are subsampled we may need to shrink the crop region by
+ // a whole texel on each side. Since skia still adds its own 0.5 inset, we apply an
+ // additional 0.5 inset. See GLConsumer::computeTransformMatrix for details.
+ float shrinkAmount = 0.0f;
+ switch (format) {
+ // Use HAL formats since some AHB formats are only available in vndk
+ case HAL_PIXEL_FORMAT_YCBCR_420_888:
+ case HAL_PIXEL_FORMAT_YV12:
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ shrinkAmount = 0.5f;
+ break;
+ default:
+ break;
+ }
+
+ // Shrink the crop if it has more than 1-px and differs from the buffer size.
+ if (cropRect->width() > 1 && cropRect->width() < bufferWidth) {
+ cropRect->inset(shrinkAmount, 0);
+ }
+
+ if (cropRect->height() > 1 && cropRect->height() < bufferHeight) {
+ cropRect->inset(0, shrinkAmount);
+ }
+}
+
// TODO: Context arg probably doesn't belong here – do debug check at callsite instead.
bool LayerDrawable::DrawLayer(GrRecordingContext* context,
SkCanvas* canvas,
@@ -142,6 +168,7 @@ bool LayerDrawable::DrawLayer(GrRecordingContext* context,
SkRect skiaSrcRect;
if (srcRect && !srcRect->isEmpty()) {
skiaSrcRect = *srcRect;
+ adjustCropForYUV(layer->getBufferFormat(), imageWidth, imageHeight, &skiaSrcRect);
} else {
skiaSrcRect = SkRect::MakeIWH(imageWidth, imageHeight);
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 32015b87f487..9b81c09f5c5b 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -2103,7 +2103,7 @@ public class LocationManager {
try {
mService.addTestProvider(provider, properties, new ArrayList<>(extraAttributionTags),
- mContext.getOpPackageName(), mContext.getFeatureId());
+ mContext.getOpPackageName(), mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2125,7 +2125,7 @@ public class LocationManager {
try {
mService.removeTestProvider(provider, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2160,7 +2160,7 @@ public class LocationManager {
try {
mService.setTestProviderLocation(provider, location, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2190,7 +2190,7 @@ public class LocationManager {
try {
mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index c63a5d8d36a2..30f4562bbf28 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -335,13 +335,13 @@ final public class MediaMuxer {
}
/**
- * Constructor.
* Creates a media muxer that writes to the specified path.
+ * <p>The caller must not use the file {@code path} before calling {@link #stop}.
* @param path The path of the output media file.
* @param format The format of the output media file.
* @see android.media.MediaMuxer.OutputFormat
* @throws IllegalArgumentException if path is invalid or format is not supported.
- * @throws IOException if failed to open the file for write.
+ * @throws IOException if an error occurs while opening or creating the output file.
*/
public MediaMuxer(@NonNull String path, @Format int format) throws IOException {
if (path == null) {
@@ -363,16 +363,19 @@ final public class MediaMuxer {
}
/**
- * Constructor.
- * Creates a media muxer that writes to the specified FileDescriptor. File descriptor
- * must be seekable and writable. Application should not use the file referenced
- * by this file descriptor until {@link #stop}. It is the application's responsibility
- * to close the file descriptor. It is safe to do so as soon as this call returns.
- * @param fd The FileDescriptor of the output media file.
+ * Creates a media muxer that writes to the specified FileDescriptor.
+ * <p>The caller must not use the file referenced by the specified {@code fd} before calling
+ * {@link #stop}.
+ * <p>It is the caller's responsibility to close the file descriptor, which is safe to do so
+ * as soon as this call returns.
+ * @param fd The FileDescriptor of the output media file. If {@code format} is
+ * {@link OutputFormat#MUXER_OUTPUT_WEBM}, {@code fd} must be open in read-write mode.
+ * Otherwise, write mode is sufficient, but read-write is also accepted.
* @param format The format of the output media file.
* @see android.media.MediaMuxer.OutputFormat
- * @throws IllegalArgumentException if fd is invalid or format is not supported.
- * @throws IOException if failed to open the file for write.
+ * @throws IllegalArgumentException if {@code format} is not supported, or if {@code fd} is
+ * not open in the expected mode.
+ * @throws IOException if an error occurs while performing an IO operation.
*/
public MediaMuxer(@NonNull FileDescriptor fd, @Format int format) throws IOException {
setUpMediaMuxer(fd, format);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index aeb81c12ad6d..77b574695ede 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2528,10 +2528,20 @@ public class MediaPlayer extends PlayerBase
}
/**
+ * Returns whether this track contains haptic channels in the audio track.
+ * @hide
+ */
+ public boolean hasHapticChannels() {
+ return mFormat != null && mFormat.containsKey(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT)
+ && mFormat.getInteger(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT) > 0;
+ }
+
+ /**
* Gets the {@link MediaFormat} of the track. If the format is
* unknown or could not be determined, null is returned.
*/
public MediaFormat getFormat() {
+ // Note: The format isn't exposed for audio because it is incomplete.
if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT
|| mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
return mFormat;
@@ -2574,6 +2584,11 @@ public class MediaPlayer extends PlayerBase
mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt());
mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt());
mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt());
+ } else if (mTrackType == MEDIA_TRACK_TYPE_AUDIO) {
+ boolean hasHapticChannels = in.readBoolean();
+ if (hasHapticChannels) {
+ mFormat.setInteger(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT, in.readInt());
+ }
}
}
@@ -2604,6 +2619,13 @@ public class MediaPlayer extends PlayerBase
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE));
+ } else if (mTrackType == MEDIA_TRACK_TYPE_AUDIO) {
+ boolean hasHapticChannels =
+ mFormat.containsKey(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT);
+ dest.writeBoolean(hasHapticChannels);
+ if (hasHapticChannels) {
+ dest.writeInt(mFormat.getInteger(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT));
+ }
}
}
diff --git a/media/java/android/media/tv/tuner/TunerVersionChecker.java b/media/java/android/media/tv/tuner/TunerVersionChecker.java
index 3e13bed0afbd..f29a93cbb228 100644
--- a/media/java/android/media/tv/tuner/TunerVersionChecker.java
+++ b/media/java/android/media/tv/tuner/TunerVersionChecker.java
@@ -59,6 +59,10 @@ public final class TunerVersionChecker {
* Tuner version 2.0.
*/
public static final int TUNER_VERSION_2_0 = (2 << 16);
+ /**
+ * Tuner version 3.0.
+ */
+ public static final int TUNER_VERSION_3_0 = (3 << 16);
/**
* Get the current running Tuner version.
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index 11e699981e80..06802409baf7 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -23,6 +23,7 @@ import android.annotation.SystemApi;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
+import android.media.tv.tuner.TunerVersionChecker;
import android.media.tv.tuner.filter.Filter;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -91,6 +92,7 @@ public class DvrPlayback implements AutoCloseable {
private native int nativeAttachFilter(Filter filter);
private native int nativeDetachFilter(Filter filter);
private native int nativeConfigureDvr(DvrSettings settings);
+ private native int nativeSetStatusCheckIntervalHint(long durationInMs);
private native int nativeStartDvr();
private native int nativeStopDvr();
private native int nativeFlushDvr();
@@ -177,6 +179,35 @@ public class DvrPlayback implements AutoCloseable {
}
/**
+ * Set playback buffer status check time interval.
+ *
+ * This status check time interval will be used by the Dvr to decide how often to evaluate
+ * data. The default value will be decided by HAL if it’s not set.
+ *
+ * <p>This functionality is only available in Tuner version 3.0 and higher and will otherwise
+ * return a {@link Tuner#RESULT_UNAVAILABLE}. Use {@link TunerVersionChecker#getTunerVersion()}
+ * to get the version information.
+ *
+ * @param durationInMs specifies the duration of the delay in milliseconds.
+ *
+ * @return one of the following results:
+ * {@link Tuner#RESULT_SUCCESS} if succeed,
+ * {@link Tuner#RESULT_UNAVAILABLE} if Dvr is unavailable or unsupported HAL versions,
+ * {@link Tuner#RESULT_NOT_INITIALIZED} if Dvr is not initialized,
+ * {@link Tuner#RESULT_INVALID_STATE} if Dvr is in a wrong state,
+ * {@link Tuner#RESULT_INVALID_ARGUMENT} if the input parameter is invalid.
+ */
+ @Result
+ public int setPlaybackBufferStatusCheckIntervalHint(long durationInMs) {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "Set status check interval hint")) {
+ // no-op
+ return Tuner.RESULT_UNAVAILABLE;
+ }
+ return nativeSetStatusCheckIntervalHint(durationInMs);
+ }
+
+ /**
* Starts DVR.
*
* <p>Starts consuming playback data or producing data for recording.
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index e72026aab992..1a658320a1a9 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -22,6 +22,7 @@ import android.annotation.SystemApi;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
+import android.media.tv.tuner.TunerVersionChecker;
import android.media.tv.tuner.filter.Filter;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -53,6 +54,7 @@ public class DvrRecorder implements AutoCloseable {
private native int nativeAttachFilter(Filter filter);
private native int nativeDetachFilter(Filter filter);
private native int nativeConfigureDvr(DvrSettings settings);
+ private native int nativeSetStatusCheckIntervalHint(long durationInMs);
private native int nativeStartDvr();
private native int nativeStopDvr();
private native int nativeFlushDvr();
@@ -131,6 +133,35 @@ public class DvrRecorder implements AutoCloseable {
}
/**
+ * Set record buffer status check time interval.
+ *
+ * This status check time interval will be used by the Dvr to decide how often to evaluate
+ * data. The default value will be decided by HAL if it’s not set.
+ *
+ * <p>This functionality is only available in Tuner version 3.0 and higher and will otherwise
+ * return a {@link Tuner#RESULT_UNAVAILABLE}. Use {@link TunerVersionChecker#getTunerVersion()}
+ * to get the version information.
+ *
+ * @param durationInMs specifies the duration of the delay in milliseconds.
+ *
+ * @return one of the following results:
+ * {@link Tuner#RESULT_SUCCESS} if succeed,
+ * {@link Tuner#RESULT_UNAVAILABLE} if Dvr is unavailable or unsupported HAL versions,
+ * {@link Tuner#RESULT_NOT_INITIALIZED} if Dvr is not initialized,
+ * {@link Tuner#RESULT_INVALID_STATE} if Dvr is in a wrong state,
+ * {@link Tuner#RESULT_INVALID_ARGUMENT} if the input parameter is invalid.
+ */
+ @Result
+ public int setRecordBufferStatusCheckIntervalHint(long durationInMs) {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "Set status check interval hint")) {
+ // no-op
+ return Tuner.RESULT_UNAVAILABLE;
+ }
+ return nativeSetStatusCheckIntervalHint(durationInMs);
+ }
+
+ /**
* Starts DVR.
*
* <p>Starts consuming playback data or producing data for recording.
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 96a3781af3a8..a0304bb5c04b 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -175,7 +175,7 @@ cc_library_shared {
shared_libs: [
"android.hardware.graphics.bufferqueue@2.0",
- "android.hardware.tv.tuner-V1-ndk",
+ "android.hardware.tv.tuner-V2-ndk",
"libbinder_ndk",
"libandroid_runtime",
"libcutils",
diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp
index 267917653efb..b664325ab0d1 100644
--- a/media/jni/android_media_MediaMuxer.cpp
+++ b/media/jni/android_media_MediaMuxer.cpp
@@ -165,7 +165,11 @@ static jlong android_media_MediaMuxer_native_setup(
MediaMuxer::OutputFormat fileFormat =
static_cast<MediaMuxer::OutputFormat>(format);
- sp<MediaMuxer> muxer = new MediaMuxer(fd, fileFormat);
+ sp<MediaMuxer> muxer = MediaMuxer::create(fd, fileFormat);
+ if (muxer == nullptr) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Muxer creation failed");
+ return 0;
+ }
muxer->incStrong(clazz);
return reinterpret_cast<jlong>(muxer.get());
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 244730b76df2..c18edcd8689b 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -4485,6 +4485,17 @@ static jint android_media_tv_Tuner_configure_dvr(JNIEnv *env, jobject dvr, jobje
return (jint)result;
}
+static jint android_media_tv_Tuner_set_status_check_interval_hint(JNIEnv *env, jobject dvr,
+ jlong durationInMs) {
+ sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+ if (dvrClient == nullptr) {
+ ALOGD("Failed to set status check interval hint: dvr client not found");
+ return (int)Result::NOT_INITIALIZED;
+ }
+ Result result = dvrClient->setStatusCheckIntervalHint(durationInMs);
+ return (jint)result;
+}
+
static jint android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
if (dvrClient == nullptr) {
@@ -4828,6 +4839,8 @@ static const JNINativeMethod gDvrRecorderMethods[] = {
(void *)android_media_tv_Tuner_detach_filter },
{ "nativeConfigureDvr", "(Landroid/media/tv/tuner/dvr/DvrSettings;)I",
(void *)android_media_tv_Tuner_configure_dvr },
+ { "nativeSetStatusCheckIntervalHint", "(J)I",
+ (void *)android_media_tv_Tuner_set_status_check_interval_hint},
{ "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 },
@@ -4844,6 +4857,8 @@ static const JNINativeMethod gDvrPlaybackMethods[] = {
(void *)android_media_tv_Tuner_detach_filter},
{ "nativeConfigureDvr", "(Landroid/media/tv/tuner/dvr/DvrSettings;)I",
(void *)android_media_tv_Tuner_configure_dvr},
+ { "nativeSetStatusCheckIntervalHint", "(J)I",
+ (void *)android_media_tv_Tuner_set_status_check_interval_hint},
{ "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},
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
index 05683b661716..6e4705251724 100644
--- a/media/jni/tuner/DvrClient.cpp
+++ b/media/jni/tuner/DvrClient.cpp
@@ -303,7 +303,17 @@ Result DvrClient::close() {
return Result::INVALID_STATE;
}
+Result DvrClient::setStatusCheckIntervalHint(int64_t durationInMs) {
+ if (mTunerDvr == nullptr) {
+ return Result::INVALID_STATE;
+ }
+ if (durationInMs < 0) {
+ return Result::INVALID_ARGUMENT;
+ }
+ Status s = mTunerDvr->setStatusCheckIntervalHint(durationInMs);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+}
/////////////// TunerDvrCallback ///////////////////////
TunerDvrCallback::TunerDvrCallback(sp<DvrClientCallback> dvrClientCallback)
: mDvrClientCallback(dvrClientCallback) {}
diff --git a/media/jni/tuner/DvrClient.h b/media/jni/tuner/DvrClient.h
index 61c0325813a4..40ed75b365c3 100644
--- a/media/jni/tuner/DvrClient.h
+++ b/media/jni/tuner/DvrClient.h
@@ -126,6 +126,11 @@ public:
*/
Result close();
+ /**
+ * Set status check time interval.
+ */
+ Result setStatusCheckIntervalHint(int64_t durationInMs);
+
private:
/**
* An AIDL Tuner Dvr Singleton assigned at the first time the Tuner Client
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index a0d449239b2a..ea0494f2a8f1 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -89,7 +89,7 @@ public class MediaInserterTest extends InstrumentationTestCase {
MockitoAnnotations.initMocks(this);
final Context attributionContext = getInstrumentation().getContext()
- .createFeatureContext(TEST_FEATURE_ID);
+ .createAttributionContext(TEST_FEATURE_ID);
final ContentProviderClient client = new ContentProviderClient(attributionContext
.getContentResolver(), mMockProvider, true);
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 21d4d8023e54..891379977a6c 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -139,8 +139,18 @@ void ASurfaceControl_release(ASurfaceControl* aSurfaceControl) {
}
ASurfaceControl* ASurfaceControl_fromSurfaceControl(JNIEnv* env, jobject surfaceControlObj) {
- return reinterpret_cast<ASurfaceControl*>(
- android_view_SurfaceControl_getNativeSurfaceControl(env, surfaceControlObj));
+ LOG_ALWAYS_FATAL_IF(!env,
+ "nullptr passed to ASurfaceControl_fromSurfaceControl as env argument");
+ LOG_ALWAYS_FATAL_IF(!surfaceControlObj,
+ "nullptr passed to ASurfaceControl_fromSurfaceControl as surfaceControlObj "
+ "argument");
+ SurfaceControl* surfaceControl =
+ android_view_SurfaceControl_getNativeSurfaceControl(env, surfaceControlObj);
+ LOG_ALWAYS_FATAL_IF(!surfaceControl,
+ "surfaceControlObj passed to ASurfaceControl_fromSurfaceControl is not "
+ "valid");
+ SurfaceControl_acquire(surfaceControl);
+ return reinterpret_cast<ASurfaceControl*>(surfaceControl);
}
struct ASurfaceControlStats {
@@ -200,8 +210,17 @@ void ASurfaceTransaction_delete(ASurfaceTransaction* aSurfaceTransaction) {
}
ASurfaceTransaction* ASurfaceTransaction_fromTransaction(JNIEnv* env, jobject transactionObj) {
- return reinterpret_cast<ASurfaceTransaction*>(
- android_view_SurfaceTransaction_getNativeSurfaceTransaction(env, transactionObj));
+ LOG_ALWAYS_FATAL_IF(!env,
+ "nullptr passed to ASurfaceTransaction_fromTransaction as env argument");
+ LOG_ALWAYS_FATAL_IF(!transactionObj,
+ "nullptr passed to ASurfaceTransaction_fromTransaction as transactionObj "
+ "argument");
+ Transaction* transaction =
+ android_view_SurfaceTransaction_getNativeSurfaceTransaction(env, transactionObj);
+ LOG_ALWAYS_FATAL_IF(!transaction,
+ "surfaceControlObj passed to ASurfaceTransaction_fromTransaction is not "
+ "valid");
+ return reinterpret_cast<ASurfaceTransaction*>(transaction);
}
void ASurfaceTransaction_apply(ASurfaceTransaction* aSurfaceTransaction) {
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 9818ee78b2b1..56715b422069 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -46,6 +46,7 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.companion.AssociatedDevice;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
@@ -331,6 +332,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements
private void onUserSelectedDevice(@NonNull DeviceFilterPair<?> selectedDevice) {
final MacAddress macAddress = selectedDevice.getMacAddress();
mRequest.setDisplayName(selectedDevice.getDisplayName());
+ mRequest.setAssociatedDevice(new AssociatedDevice(selectedDevice.getDevice()));
onAssociationApproved(macAddress);
}
diff --git a/packages/SettingsLib/SpaPrivileged/Android.bp b/packages/SettingsLib/SpaPrivileged/Android.bp
index e7e37e418185..8edd5f7b52c6 100644
--- a/packages/SettingsLib/SpaPrivileged/Android.bp
+++ b/packages/SettingsLib/SpaPrivileged/Android.bp
@@ -30,7 +30,7 @@ android_library {
],
kotlincflags: [
"-Xjvm-default=all",
- "-Xopt-in=kotlin.RequiresOptIn",
+ "-opt-in=kotlin.RequiresOptIn",
],
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt
index 7796549485b6..1dc52cbfb4ec 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt
@@ -1,6 +1,7 @@
package com.android.settingslib.spaprivileged.framework.common
import android.app.admin.DevicePolicyManager
+import android.app.usage.StorageStatsManager
import android.content.Context
import android.os.UserManager
@@ -9,3 +10,6 @@ val Context.userManager get() = getSystemService(UserManager::class.java)!!
/** The [DevicePolicyManager] instance. */
val Context.devicePolicyManager get() = getSystemService(DevicePolicyManager::class.java)!!
+
+/** The [StorageStatsManager] instance. */
+val Context.storageStatsManager get() = getSystemService(StorageStatsManager::class.java)!!
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
new file mode 100644
index 000000000000..037b737ba373
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 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.settingslib.spaprivileged.template.app
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.text.format.Formatter
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.produceState
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
+import com.android.settingslib.spaprivileged.model.app.userHandle
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+@Composable
+fun ApplicationInfo.getStorageSize(): State<String> {
+ val context = LocalContext.current
+ return produceState(initialValue = stringResource(R.string.summary_placeholder)) {
+ withContext(Dispatchers.IO) {
+ value = Formatter.formatFileSize(context, calculateSizeBytes(context))
+ }
+ }
+}
+
+private fun ApplicationInfo.calculateSizeBytes(context: Context): Long {
+ val storageStatsManager = context.storageStatsManager
+ val stats = storageStatsManager.queryStatsForPackage(storageUuid, packageName, userHandle)
+ return stats.codeBytes + stats.dataBytes + stats.cacheBytes
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
new file mode 100644
index 000000000000..cec6d7d5d760
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 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.settingslib.spaprivileged.template.app
+
+import android.app.usage.StorageStats
+import android.app.usage.StorageStatsManager
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
+import com.android.settingslib.spaprivileged.model.app.userHandle
+import com.google.common.truth.Truth.assertThat
+import java.util.UUID
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class AppStorageSizeTest {
+ @JvmField
+ @Rule
+ val mockito: MockitoRule = MockitoJUnit.rule()
+
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Spy
+ private var context: Context = ApplicationProvider.getApplicationContext()
+
+ @Mock
+ private lateinit var storageStatsManager: StorageStatsManager
+
+ private val app = ApplicationInfo().apply {
+ storageUuid = UUID.randomUUID()
+ }
+
+ @Before
+ fun setUp() {
+ whenever(context.storageStatsManager).thenReturn(storageStatsManager)
+ whenever(storageStatsManager.queryStatsForPackage(
+ app.storageUuid, app.packageName, app.userHandle
+ )).thenReturn(STATS)
+ }
+
+ @Test
+ fun getStorageSize() {
+ var storageSize = stateOf("")
+
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalContext provides context) {
+ storageSize = app.getStorageSize()
+ }
+ }
+
+ assertThat(storageSize.value).isEqualTo("123 B")
+ }
+
+ companion object {
+ private val STATS = StorageStats().apply {
+ codeBytes = 100
+ dataBytes = 20
+ cacheBytes = 3
+ }
+ }
+}
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 705cccbbd0d7..3b00d26fe793 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -106,7 +106,7 @@
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"المكالمات الهاتفية"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"نقل الملف"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"جهاز الإرسال"</string>
- <string name="bluetooth_profile_pan" msgid="1006235139308318188">"استخدام الإنترنت"</string>
+ <string name="bluetooth_profile_pan" msgid="1006235139308318188">"الوصول إلى الإنترنت"</string>
<string name="bluetooth_profile_pbap" msgid="4262303387989406171">"مشاركة جهات الاتصال وسجل المكالمات"</string>
<string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"استخدام إعدادات بلوتوث لمشاركة جهات الاتصال وسجل المكالمات"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"مشاركة اتصال الإنترنت"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index ba0d30283443..e13ffce7cdd5 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -70,7 +70,7 @@
<string name="wifi_limited_connection" msgid="1184778285475204682">"ইণ্টাৰনেট সংযোগ সীমিত"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ইণ্টাৰনেট সংযোগ নাই"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"ছাইন ইন কৰা দৰকাৰী"</string>
- <string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"একচেছ পইণ্ট কিছু সময়ৰ বাবে পূৰ্ণ হৈ আছে"</string>
+ <string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"এক্সেছ পইণ্ট সাময়িকভাৱে পূৰ্ণ হৈ আছে"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> খুলি থকা হৈছে"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"সংযোগ কৰিব পৰা নগ’ল"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"ছাইন আপ সম্পূৰ্ণ কৰি থকা হৈছে…"</string>
@@ -129,7 +129,7 @@
<string name="bluetooth_pan_nap_profile_summary_connected" msgid="3744773111299503493">"ডিভাইচৰ সৈতে স্থানীয় ইণ্টাৰনেট সংযোগ শ্বেয়াৰ কৰা হৈছে"</string>
<string name="bluetooth_pan_profile_summary_use_for" msgid="7422039765025340313">"ইণ্টাৰনেট চলাবলৈ ব্যৱহাৰ কৰক"</string>
<string name="bluetooth_map_profile_summary_use_for" msgid="4453622103977592583">"মেপৰ বাবে ব্যৱহাৰ কৰক"</string>
- <string name="bluetooth_sap_profile_summary_use_for" msgid="6204902866176714046">"ছিমত প্ৰৱেশৰ বাবে ব্যৱহাৰ কৰক"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="6204902866176714046">"ছিমৰ এক্সেছৰ বাবে ব্যৱহাৰ কৰক"</string>
<string name="bluetooth_a2dp_profile_summary_use_for" msgid="7324694226276491807">"মিডিয়া অডিঅ\'ৰ বাবে ব্যৱহাৰ কৰক"</string>
<string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ফ\'ন অডিঅ\'ৰ বাবে ব্যৱহাৰ কৰক"</string>
<string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ফাইল স্থানান্তৰ কৰিবলৈ ব্যৱহাৰ কৰক"</string>
@@ -139,7 +139,7 @@
<string name="bluetooth_pairing_accept" msgid="2054232610815498004">"পেয়াৰ কৰক"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"পেয়াৰ কৰক"</string>
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"বাতিল কৰক"</string>
- <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"যোৰা লগালে ইয়ে সংযোজিত কৰাৰ সময়ত আপোনাৰ সম্পৰ্কসমূহ আৰু কলৰ ইতিহাস চাবলৈ অনুমতি দিব।"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"পেয়াৰিঙে সংযোজিত হ\'লে আপোনাৰ সম্পৰ্ক আৰু কলৰ ইতিহাসৰ এক্সেছ প্ৰদান কৰে।"</string>
<string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>ৰ সৈতে পেয়াৰ কৰিব পৰা নগ’ল।"</string>
<string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"এটা ভুল পিন বা পাছকীৰ কাৰণে <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ৰ সৈতে পেয়াৰ কৰিব পৰা নাই।"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>ৰ সৈতে যোগাযোগ কৰিব পৰা নগ\'ল"</string>
@@ -332,7 +332,7 @@
<string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ব্লুটুথ Gabeldorche সুবিধাৰ সমষ্টিটো সক্ষম কৰে।"</string>
<string name="enhanced_connectivity_summary" msgid="1576414159820676330">"উন্নত সংযোগ সুবিধাটো সক্ষম কৰে।"</string>
<string name="enable_terminal_title" msgid="3834790541986303654">"স্থানীয় টাৰ্মিনেল"</string>
- <string name="enable_terminal_summary" msgid="2481074834856064500">"স্থানীয় শ্বেল প্ৰৱেশাধিকাৰ দিয়া টাৰ্মিনেল এপ্ সক্ষম কৰক"</string>
+ <string name="enable_terminal_summary" msgid="2481074834856064500">"স্থানীয় শ্বেলৰ এক্সেছ দিয়া টাৰ্মিনেল এপ্ সক্ষম কৰক"</string>
<string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP পৰীক্ষণ"</string>
<string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP পৰীক্ষণ আচৰণ ছেট কৰক"</string>
<string name="debug_debugging_category" msgid="535341063709248842">"ডিবাগিং"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 8e8150cf5a24..29d7d95350e5 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -258,7 +258,7 @@
<string name="keep_screen_on" msgid="1187161672348797558">"Mantendu aktibo"</string>
<string name="keep_screen_on_summary" msgid="1510731514101925829">"Pantaila ez da ezarriko inoiz inaktibo kargatu bitartean"</string>
<string name="bt_hci_snoop_log" msgid="7291287955649081448">"Gaitu Bluetooth HCI miatze-erregistroa"</string>
- <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Hauteman Bluetooth paketeak (aktibatu edo desaktibatu Bluetooth-a ezarpena aldatu ostean)"</string>
+ <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Hauteman Bluetooth paketeak (aktibatu edo desaktibatu Bluetootha ezarpena aldatu ostean)"</string>
<string name="oem_unlock_enable" msgid="5334869171871566731">"OEM desblokeoa"</string>
<string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Onartu abiarazlea desblokeatzea"</string>
<string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"OEM desblokeoa onartu nahi duzu?"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 3d1e98d101d7..c292e8817c69 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -151,12 +151,12 @@
<string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Øretelefoner"</string>
<string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Inndata fra ytre utstyrsenheter"</string>
<string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string>
- <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi er av."</string>
- <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi er frakoblet."</string>
- <string name="accessibility_wifi_one_bar" msgid="6025652717281815212">"Wi-Fi-signal med én stolpe."</string>
- <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi-signal med to stolper."</string>
- <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi-signal med tre stolper."</string>
- <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi-signalet er ved full styrke."</string>
+ <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wifi er av."</string>
+ <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wifi er frakoblet."</string>
+ <string name="accessibility_wifi_one_bar" msgid="6025652717281815212">"Wifi-signal med én stolpe."</string>
+ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi-signal med to stolper."</string>
+ <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi-signal med tre stolper."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi-signalet er ved full styrke."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Åpent nettverk"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sikkert nettverk"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android-operativsystem"</string>
@@ -226,7 +226,7 @@
<string name="enable_adb_summary" msgid="3711526030096574316">"Feilsøkingsmodus når USB kobles til"</string>
<string name="clear_adb_keys" msgid="3010148733140369917">"USB-feilsøking – opphev autorisasjon"</string>
<string name="enable_adb_wireless" msgid="6973226350963971018">"Trådløs feilsøking"</string>
- <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Feilsøkingsmodus når Wi-Fi er tilkoblet"</string>
+ <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Feilsøkingsmodus når Wifi er tilkoblet"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"Feil"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"Trådløs feilsøking"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"For å se og bruke tilgjengelige enheter, slå på trådløs feilsøking"</string>
@@ -245,13 +245,13 @@
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi-tilkoblingskode"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Tilkoblingen mislyktes"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Sørg for at enheten er koblet til samme nettverk."</string>
- <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Koble til enheten via Wi-Fi ved å skanne en QR-kode"</string>
+ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Koble til enheten via Wifi ved å skanne en QR-kode"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Kobler til enheten …"</string>
<string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Kunne ikke koble til enheten. Enten var QR-koden feil, eller enheten er ikke koblet til samme nettverk."</string>
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP-adresse og port"</string>
<string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Skann QR-koden"</string>
- <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Koble til enheten via Wi-Fi ved å skanne en QR-kode"</string>
- <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Koble til et Wi-Fi-nettverk"</string>
+ <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Koble til enheten via Wifi ved å skanne en QR-kode"</string>
+ <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Koble til et Wifi-nettverk"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, feilsøking, utvikler"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Snarvei til feilrapport"</string>
<string name="bugreport_in_power_summary" msgid="1885529649381831775">"Vis en knapp for generering av feilrapport i batterimenyen"</string>
@@ -268,7 +268,7 @@
<string name="mock_location_app_set" msgid="4706722469342913843">"App for fiktiv posisjon: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="debug_networking_category" msgid="6829757985772659599">"Nettverk"</string>
<string name="wifi_display_certification" msgid="1805579519992520381">"Trådløs skjerm-sertifisering"</string>
- <string name="wifi_verbose_logging" msgid="1785910450009679371">"Slå på detaljert Wi-Fi-loggføring"</string>
+ <string name="wifi_verbose_logging" msgid="1785910450009679371">"Slå på detaljert Wifi-loggføring"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Begrensning av Wi‑Fi-skanning"</string>
<string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Ikke-vedvarende tilfeldiggjøring av MAC-adresse for Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobildata er alltid aktiv"</string>
@@ -300,7 +300,7 @@
<string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"Skriv inn DNS-leverandørens vertsnavn"</string>
<string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"Kunne ikke koble til"</string>
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Vis alternativer for sertifisering av trådløs skjerm"</string>
- <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Øk nivået av Wi-Fi-logging – vis per SSID RSSI i Wi-Fi-velgeren"</string>
+ <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Øk nivået av Wifi-logging – vis per SSID RSSI i Wifi-velgeren"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduserer batteriforbruket og forbedrer nettverksytelsen"</string>
<string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Når denne modusen er slått på, kan MAC-adressen til denne enheten endres hver gang den kobler seg til et nettverk som har tilfeldiggjøring av MAC-adresse slått på."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Med datamåling"</string>
@@ -316,7 +316,7 @@
<string name="allow_mock_location" msgid="2102650981552527884">"Tillat simulert posisjon"</string>
<string name="allow_mock_location_summary" msgid="179780881081354579">"Tillat bruk av simulerte GPS-koordinater"</string>
<string name="debug_view_attributes" msgid="3539609843984208216">"Slå på inspeksjon av visningsattributt"</string>
- <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Ha alltid mobildata slått på, selv når Wi-Fi er aktiv (for hurtig nettverksbytting)."</string>
+ <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Ha alltid mobildata slått på, selv når Wifi er aktiv (for hurtig nettverksbytting)."</string>
<string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Bruk maskinvareakselerasjon for internettdeling hvis det er tilgjengelig"</string>
<string name="adb_warning_title" msgid="7708653449506485728">"Tillate USB-feilsøking?"</string>
<string name="adb_warning_message" msgid="8145270656419669221">"USB-feilsøking er bare ment for utviklingsformål. Bruk det til å kopiere data mellom datamaskinen og enheten, installere apper på enheten uten varsel og lese loggdata."</string>
@@ -561,7 +561,7 @@
<string name="user_add_user_item_title" msgid="2394272381086965029">"Bruker"</string>
<string name="user_add_profile_item_title" msgid="3111051717414643029">"Begrenset profil"</string>
<string name="user_add_user_title" msgid="5457079143694924885">"Vil du legge til en ny bruker?"</string>
- <string name="user_add_user_message_long" msgid="1527434966294733380">"Du kan dele denne enheten med andre folk ved å opprette flere brukere. Hver bruker har sin egen plass de kan tilpasse med apper, bakgrunner og annet. Brukere kan også justere enhetsinnstillinger, for eksempel Wi-Fi, som påvirker alle.\n\nNår du legger til en ny bruker, må vedkommende angi innstillinger for plassen sin.\n\nAlle brukere kan oppdatere apper for alle andre brukere. Innstillinger og tjenester for tilgjengelighet overføres kanskje ikke til den nye brukeren."</string>
+ <string name="user_add_user_message_long" msgid="1527434966294733380">"Du kan dele denne enheten med andre folk ved å opprette flere brukere. Hver bruker har sin egen plass de kan tilpasse med apper, bakgrunner og annet. Brukere kan også justere enhetsinnstillinger, for eksempel Wifi, som påvirker alle.\n\nNår du legger til en ny bruker, må vedkommende angi innstillinger for plassen sin.\n\nAlle brukere kan oppdatere apper for alle andre brukere. Innstillinger og tjenester for tilgjengelighet overføres kanskje ikke til den nye brukeren."</string>
<string name="user_add_user_message_short" msgid="3295959985795716166">"Når du legger til en ny bruker, må hen konfigurere sitt eget område.\n\nAlle brukere kan oppdatere apper for alle andre brukere."</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"Konfigurere brukeren nå?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"Sørg for at brukeren er tilgjengelig for å konfigurere området sitt på enheten"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 8c12372ff7d1..17a06c21d4fa 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -86,7 +86,7 @@
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Kopplar ifrån…"</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"Ansluter…"</string>
<string name="bluetooth_connected" msgid="8065345572198502293">"Ansluten<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_pairing" msgid="4269046942588193600">"Parkoppling…"</string>
+ <string name="bluetooth_pairing" msgid="4269046942588193600">"Parkopplar…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Ansluten (ingen mobil)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Ansluten (inga medier)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Ansluten (ingen mobil och inga medier) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
index eff9e74e0e70..ee65ef4e92b6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
@@ -22,8 +22,11 @@ package com.android.settingslib;
public class AccessibilityContentDescriptions {
private AccessibilityContentDescriptions() {}
+
+ public static final int PHONE_SIGNAL_STRENGTH_NONE = R.string.accessibility_no_phone;
+
public static final int[] PHONE_SIGNAL_STRENGTH = {
- R.string.accessibility_no_phone,
+ PHONE_SIGNAL_STRENGTH_NONE,
R.string.accessibility_phone_one_bar,
R.string.accessibility_phone_two_bars,
R.string.accessibility_phone_three_bars,
diff --git a/packages/SettingsLib/src/com/android/settingslib/MobileNetworkTypeIcon.kt b/packages/SettingsLib/src/com/android/settingslib/MobileNetworkTypeIcon.kt
new file mode 100644
index 000000000000..3daf8c20d241
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/MobileNetworkTypeIcon.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 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.settingslib
+
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+
+/**
+ * A specification for the icon displaying the mobile network type -- 4G, 5G, LTE, etc. (aka "RAT
+ * icon" or "data type icon"). This is *not* the signal strength triangle.
+ *
+ * This is intended to eventually replace [SignalIcon.MobileIconGroup]. But for now,
+ * [MobileNetworkTypeIcons] just reads from the existing set of [SignalIcon.MobileIconGroup]
+ * instances to not duplicate data.
+ *
+ * TODO(b/238425913): Remove [SignalIcon.MobileIconGroup] and replace it with this class so that we
+ * don't need to fill in the superfluous fields from its parent [SignalIcon.IconGroup] class. Then
+ * this class can become either a sealed class or an enum with parameters.
+ */
+data class MobileNetworkTypeIcon(
+ /** A human-readable name for this network type, used for logging. */
+ val name: String,
+
+ /** The resource ID of the icon drawable to use. */
+ @DrawableRes val iconResId: Int,
+
+ /** The resource ID of the content description to use. */
+ @StringRes val contentDescriptionResId: Int,
+)
diff --git a/packages/SettingsLib/src/com/android/settingslib/MobileNetworkTypeIcons.kt b/packages/SettingsLib/src/com/android/settingslib/MobileNetworkTypeIcons.kt
new file mode 100644
index 000000000000..2c5ee8902766
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/MobileNetworkTypeIcons.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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.settingslib
+
+import com.android.settingslib.mobile.TelephonyIcons.ICON_NAME_TO_ICON
+
+/**
+ * A utility class to fetch instances of [MobileNetworkTypeIcon] given a
+ * [SignalIcon.MobileIconGroup].
+ *
+ * Use [getNetworkTypeIcon] to fetch the instances.
+ */
+class MobileNetworkTypeIcons {
+ companion object {
+ /**
+ * A map from a [SignalIcon.MobileIconGroup.name] to an instance of [MobileNetworkTypeIcon],
+ * which is the preferred class going forward.
+ */
+ private val MOBILE_NETWORK_TYPE_ICONS: Map<String, MobileNetworkTypeIcon>
+
+ init {
+ // Build up the mapping from the old implementation to the new one.
+ val tempMap: MutableMap<String, MobileNetworkTypeIcon> = mutableMapOf()
+
+ ICON_NAME_TO_ICON.forEach { (_, mobileIconGroup) ->
+ tempMap[mobileIconGroup.name] = mobileIconGroup.toNetworkTypeIcon()
+ }
+
+ MOBILE_NETWORK_TYPE_ICONS = tempMap
+ }
+
+ /**
+ * A converter function between the old mobile network type icon implementation and the new
+ * one. Given an instance of the old class [mobileIconGroup], outputs an instance of the
+ * new class [MobileNetworkTypeIcon].
+ */
+ @JvmStatic
+ fun getNetworkTypeIcon(
+ mobileIconGroup: SignalIcon.MobileIconGroup
+ ): MobileNetworkTypeIcon {
+ return MOBILE_NETWORK_TYPE_ICONS[mobileIconGroup.name]
+ ?: mobileIconGroup.toNetworkTypeIcon()
+ }
+
+ private fun SignalIcon.MobileIconGroup.toNetworkTypeIcon(): MobileNetworkTypeIcon {
+ return MobileNetworkTypeIcon(
+ name = this.name,
+ iconResId = this.dataType,
+ contentDescriptionResId = this.dataContentDescription
+ )
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java b/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
index 280e40726c03..6aaab3c56480 100644
--- a/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
+++ b/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
@@ -15,6 +15,9 @@
*/
package com.android.settingslib;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.StringRes;
+
/**
* Icons for SysUI and Settings.
*/
@@ -66,34 +69,31 @@ public class SignalIcon {
}
/**
- * Holds icons for a given MobileState.
+ * Holds RAT icons for a given MobileState.
*/
public static class MobileIconGroup extends IconGroup {
- public final int dataContentDescription; // mContentDescriptionDataType
- public final int dataType;
+ @StringRes public final int dataContentDescription;
+ @DrawableRes public final int dataType;
public MobileIconGroup(
String name,
- int[][] sbIcons,
- int[][] qsIcons,
- int[] contentDesc,
- int sbNullState,
- int qsNullState,
- int sbDiscState,
- int qsDiscState,
- int discContentDesc,
int dataContentDesc,
int dataType
) {
super(name,
- sbIcons,
- qsIcons,
- contentDesc,
- sbNullState,
- qsNullState,
- sbDiscState,
- qsDiscState,
- discContentDesc);
+ // The rest of the values are the same for every type of MobileIconGroup, so
+ // just provide them here.
+ // TODO(b/238425913): Eventually replace with {@link MobileNetworkTypeIcon} so
+ // that we don't have to fill in these superfluous fields.
+ /* sbIcons= */ null,
+ /* qsIcons= */ null,
+ /* contentDesc= */ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ /* sbNullState= */ 0,
+ /* qsNullState= */ 0,
+ /* sbDiscState= */ 0,
+ /* qsDiscState= */ 0,
+ /* discContentDesc= */
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE);
this.dataContentDescription = dataContentDesc;
this.dataType = dataType;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
index 5e5c22a403d2..b7ef7664b2e5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
@@ -21,30 +21,28 @@ import android.permission.RuntimePermissionPresentationInfo;
import java.text.Collator;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
-public class PermissionsSummaryHelper {
+/**
+ * Helper to get the runtime permissions for an app.
+ */
+public class PermissionsSummaryHelper {
public static void getPermissionSummary(Context context, String pkg,
final PermissionsResultCallback callback) {
final PermissionControllerManager permController =
context.getSystemService(PermissionControllerManager.class);
permController.getAppPermissions(pkg, permissions -> {
- final int permissionCount = permissions.size();
- int grantedStandardCount = 0;
int grantedAdditionalCount = 0;
int requestedCount = 0;
List<CharSequence> grantedStandardLabels = new ArrayList<>();
- for (int i = 0; i < permissionCount; i++) {
- RuntimePermissionPresentationInfo permission = permissions.get(i);
+ for (RuntimePermissionPresentationInfo permission : permissions) {
requestedCount++;
if (permission.isGranted()) {
if (permission.isStandard()) {
grantedStandardLabels.add(permission.getLabel());
- grantedStandardCount++;
} else {
grantedAdditionalCount++;
}
@@ -53,23 +51,21 @@ public class PermissionsSummaryHelper {
Collator collator = Collator.getInstance();
collator.setStrength(Collator.PRIMARY);
- Collections.sort(grantedStandardLabels, collator);
+ grantedStandardLabels.sort(collator);
- callback.onPermissionSummaryResult(grantedStandardCount, requestedCount,
- grantedAdditionalCount, grantedStandardLabels);
+ callback.onPermissionSummaryResult(
+ requestedCount, grantedAdditionalCount, grantedStandardLabels);
}, null);
}
- public static abstract class PermissionsResultCallback {
- public void onAppWithPermissionsCountsResult(int standardGrantedPermissionAppCount,
- int standardUsedPermissionAppCount) {
- /* do nothing - stub */
- }
+ /**
+ * Callback for the runtime permissions result for an app.
+ */
+ public interface PermissionsResultCallback {
- public void onPermissionSummaryResult(int standardGrantedPermissionCount,
+ /** The runtime permission summary result for an app. */
+ void onPermissionSummaryResult(
int requestedPermissionCount, int additionalGrantedPermissionCount,
- List<CharSequence> grantedGroupLabels) {
- /* do nothing - stub */
- }
+ List<CharSequence> grantedGroupLabels);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java
index c2034f89e18a..0c0f5a8a808e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java
@@ -18,12 +18,9 @@ package com.android.settingslib.enterprise;
import static java.util.Objects.requireNonNull;
-import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.os.UserManager;
-import android.text.TextUtils;
import androidx.annotation.Nullable;
@@ -69,50 +66,11 @@ final class ManagedDeviceActionDisabledByAdminController
mResolveActivityChecker = requireNonNull(resolveActivityChecker);
}
- @Override
- public void setupLearnMoreButton(Context context) {
- assertInitialized();
-
- String url = mStringProvider.getLearnMoreHelpPageUrl();
-
- if (!TextUtils.isEmpty(url)
- && canLaunchHelpPageInPreferredOrCurrentUser(context, url, mPreferredUserHandle)) {
- setupLearnMoreButtonToLaunchHelpPage(context, url, mPreferredUserHandle);
- } else {
- mLauncher.setupLearnMoreButtonToShowAdminPolicies(context, mEnforcementAdminUserId,
- mEnforcedAdmin);
- }
- }
-
- private boolean canLaunchHelpPageInPreferredOrCurrentUser(
- Context context, String url, UserHandle preferredUserHandle) {
- PackageManager packageManager = context.getPackageManager();
- if (mLauncher.canLaunchHelpPage(
- packageManager, url, preferredUserHandle, mResolveActivityChecker)
- && mForegroundUserChecker.isUserForeground(context, preferredUserHandle)) {
- return true;
- }
- return mLauncher.canLaunchHelpPage(
- packageManager, url, context.getUser(), mResolveActivityChecker);
- }
-
/**
- * Sets up the "Learn more" button to launch the web help page in the {@code
- * preferredUserHandle} user. If not possible to launch it there, it sets up the button to
- * launch it in the current user instead.
+ * We don't show Learn More button in Admin-Support Dialog anymore.
*/
- private void setupLearnMoreButtonToLaunchHelpPage(
- Context context, String url, UserHandle preferredUserHandle) {
- PackageManager packageManager = context.getPackageManager();
- if (mLauncher.canLaunchHelpPage(
- packageManager, url, preferredUserHandle, mResolveActivityChecker)
- && mForegroundUserChecker.isUserForeground(context, preferredUserHandle)) {
- mLauncher.setupLearnMoreButtonToLaunchHelpPage(context, url, preferredUserHandle);
- }
- if (mLauncher.canLaunchHelpPage(
- packageManager, url, context.getUser(), mResolveActivityChecker)) {
- mLauncher.setupLearnMoreButtonToLaunchHelpPage(context, url, context.getUser());
- }
+ @Override
+ public void setupLearnMoreButton(Context context) {
}
private static boolean isUserForeground(Context context, UserHandle userHandle) {
@@ -122,25 +80,7 @@ final class ManagedDeviceActionDisabledByAdminController
@Override
public String getAdminSupportTitle(@Nullable String restriction) {
- if (restriction == null) {
- return mStringProvider.getDefaultDisabledByPolicyTitle();
- }
- switch (restriction) {
- case UserManager.DISALLOW_ADJUST_VOLUME:
- return mStringProvider.getDisallowAdjustVolumeTitle();
- case UserManager.DISALLOW_OUTGOING_CALLS:
- return mStringProvider.getDisallowOutgoingCallsTitle();
- case UserManager.DISALLOW_SMS:
- return mStringProvider.getDisallowSmsTitle();
- case DevicePolicyManager.POLICY_DISABLE_CAMERA:
- return mStringProvider.getDisableCameraTitle();
- case DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE:
- return mStringProvider.getDisableScreenCaptureTitle();
- case DevicePolicyManager.POLICY_SUSPEND_PACKAGES:
- return mStringProvider.getSuspendPackagesTitle();
- default:
- return mStringProvider.getDefaultDisabledByPolicyTitle();
- }
+ return mStringProvider.getDefaultDisabledByPolicyTitle();
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
index 23e0923d7280..094567c400a3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
@@ -16,7 +16,6 @@
package com.android.settingslib.mobile;
-import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.R;
import com.android.settingslib.SignalIcon.MobileIconGroup;
@@ -49,297 +48,129 @@ public class TelephonyIcons {
public static final MobileIconGroup CARRIER_NETWORK_CHANGE = new MobileIconGroup(
"CARRIER_NETWORK_CHANGE",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.carrier_network_change_mode,
- 0
+ /* dataType= */ 0
);
public static final MobileIconGroup THREE_G = new MobileIconGroup(
"3G",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_3g,
TelephonyIcons.ICON_3G
);
public static final MobileIconGroup WFC = new MobileIconGroup(
"WFC",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
- 0,
- 0);
+ /* dataContentDescription= */ 0,
+ /* dataType= */ 0);
public static final MobileIconGroup UNKNOWN = new MobileIconGroup(
"Unknown",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
- 0,
- 0);
+ /* dataContentDescription= */ 0,
+ /* dataType= */ 0);
public static final MobileIconGroup E = new MobileIconGroup(
"E",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_edge,
TelephonyIcons.ICON_E
);
public static final MobileIconGroup ONE_X = new MobileIconGroup(
"1X",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_cdma,
TelephonyIcons.ICON_1X
);
public static final MobileIconGroup G = new MobileIconGroup(
"G",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_gprs,
TelephonyIcons.ICON_G
);
public static final MobileIconGroup H = new MobileIconGroup(
"H",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_3_5g,
TelephonyIcons.ICON_H
);
public static final MobileIconGroup H_PLUS = new MobileIconGroup(
"H+",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_3_5g_plus,
TelephonyIcons.ICON_H_PLUS
);
public static final MobileIconGroup FOUR_G = new MobileIconGroup(
"4G",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_4g,
TelephonyIcons.ICON_4G
);
public static final MobileIconGroup FOUR_G_PLUS = new MobileIconGroup(
"4G+",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_4g_plus,
TelephonyIcons.ICON_4G_PLUS
);
public static final MobileIconGroup LTE = new MobileIconGroup(
"LTE",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_lte,
TelephonyIcons.ICON_LTE
);
public static final MobileIconGroup LTE_PLUS = new MobileIconGroup(
"LTE+",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_lte_plus,
TelephonyIcons.ICON_LTE_PLUS
);
public static final MobileIconGroup FOUR_G_LTE = new MobileIconGroup(
"4G LTE",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_4g_lte,
TelephonyIcons.ICON_4G_LTE
);
public static final MobileIconGroup FOUR_G_LTE_PLUS = new MobileIconGroup(
"4G LTE+",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_4g_lte_plus,
TelephonyIcons.ICON_4G_LTE_PLUS
);
public static final MobileIconGroup LTE_CA_5G_E = new MobileIconGroup(
"5Ge",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_5ge_html,
TelephonyIcons.ICON_5G_E
);
public static final MobileIconGroup NR_5G = new MobileIconGroup(
"5G",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_5g,
TelephonyIcons.ICON_5G
);
public static final MobileIconGroup NR_5G_PLUS = new MobileIconGroup(
"5G_PLUS",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_5g_plus,
TelephonyIcons.ICON_5G_PLUS
);
public static final MobileIconGroup DATA_DISABLED = new MobileIconGroup(
"DataDisabled",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.cell_data_off_content_description,
0
);
public static final MobileIconGroup NOT_DEFAULT_DATA = new MobileIconGroup(
"NotDefaultData",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0,
- 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.not_default_data_content_description,
- 0
+ /* dataType= */ 0
);
public static final MobileIconGroup CARRIER_MERGED_WIFI = new MobileIconGroup(
"CWF",
- /* sbIcons= */ null,
- /* qsIcons= */ null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- /* sbNullState= */ 0,
- /* qsNullState= */ 0,
- /* sbDiscState= */ 0,
- /* qsDiscState= */ 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_carrier_wifi,
TelephonyIcons.ICON_CWF
);
- // When adding a new MobileIconGround, check if the dataContentDescription has to be filtered
+ // When adding a new MobileIconGroup, check if the dataContentDescription has to be filtered
// in QSCarrier#hasValidTypeContentDescription
/** Mapping icon name(lower case) to the icon object. */
@@ -368,14 +199,6 @@ public class TelephonyIcons {
ICON_NAME_TO_ICON.put("notdefaultdata", NOT_DEFAULT_DATA);
}
- public static final int[] WIFI_CALL_STRENGTH_ICONS = {
- R.drawable.ic_wifi_call_strength_0,
- R.drawable.ic_wifi_call_strength_1,
- R.drawable.ic_wifi_call_strength_2,
- R.drawable.ic_wifi_call_strength_3,
- R.drawable.ic_wifi_call_strength_4
- };
-
public static final int[] MOBILE_CALL_STRENGTH_ICONS = {
R.drawable.ic_mobile_call_strength_0,
R.drawable.ic_mobile_call_strength_1,
@@ -384,4 +207,3 @@ public class TelephonyIcons {
R.drawable.ic_mobile_call_strength_4
};
}
-
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
index 3e7481a61f89..f5dddc39b21e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
@@ -43,6 +43,7 @@ import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupdesign.GlifLayout;
import com.google.android.setupdesign.util.ThemeHelper;
+import com.google.android.setupdesign.util.ThemeResolver;
import java.util.ArrayList;
import java.util.Arrays;
@@ -81,7 +82,14 @@ public class AvatarPickerActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setTheme(R.style.SudThemeGlifV3_DayNight);
+ boolean dayNightEnabled = ThemeHelper.isSetupWizardDayNightEnabled(this);
+ ThemeResolver themeResolver =
+ new ThemeResolver.Builder(ThemeResolver.getDefault())
+ .setDefaultTheme(ThemeHelper.getSuwDefaultTheme(this))
+ .setUseDayNight(true)
+ .build();
+ int themeResId = themeResolver.resolve("", /* suppressDayNight= */ !dayNightEnabled);
+ setTheme(themeResId);
ThemeHelper.trySetDynamicColor(this);
setContentView(R.layout.avatar_picker);
setUpButtons();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileNetworkTypeIconsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileNetworkTypeIconsTest.java
new file mode 100644
index 000000000000..39977dfa5c80
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileNetworkTypeIconsTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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.settingslib;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settingslib.mobile.TelephonyIcons;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class MobileNetworkTypeIconsTest {
+
+ @Test
+ public void getNetworkTypeIcon_hPlus_returnsHPlus() {
+ MobileNetworkTypeIcon icon =
+ MobileNetworkTypeIcons.getNetworkTypeIcon(TelephonyIcons.H_PLUS);
+
+ assertThat(icon.getName()).isEqualTo(TelephonyIcons.H_PLUS.name);
+ assertThat(icon.getIconResId()).isEqualTo(TelephonyIcons.ICON_H_PLUS);
+ }
+
+ @Test
+ public void getNetworkTypeIcon_fourG_returnsFourG() {
+ MobileNetworkTypeIcon icon =
+ MobileNetworkTypeIcons.getNetworkTypeIcon(TelephonyIcons.FOUR_G);
+
+ assertThat(icon.getName()).isEqualTo(TelephonyIcons.H_PLUS.name);
+ assertThat(icon.getIconResId()).isEqualTo(TelephonyIcons.ICON_4G);
+ }
+
+ @Test
+ public void getNetworkTypeIcon_unknown_returnsUnknown() {
+ SignalIcon.MobileIconGroup unknownGroup =
+ new SignalIcon.MobileIconGroup("testUnknownNameHere", 45, 6);
+
+ MobileNetworkTypeIcon icon = MobileNetworkTypeIcons.getNetworkTypeIcon(unknownGroup);
+
+ assertThat(icon.getName()).isEqualTo("testUnknownNameHere");
+ assertThat(icon.getIconResId()).isEqualTo(45);
+ assertThat(icon.getContentDescriptionResId()).isEqualTo(6);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
index bc9bdecb904d..7b885660ccea 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
@@ -18,10 +18,7 @@ package com.android.settingslib.enterprise;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCED_ADMIN;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCEMENT_ADMIN_USER_ID;
-import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.LEARN_MORE_ACTION_LAUNCH_HELP_PAGE;
-import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.SUPPORT_MESSAGE;
-import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.URL;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DISABLED_BY_POLICY_CONTENT;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DISABLED_BY_POLICY_TITLE;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DISALLOW_ADJUST_VOLUME_TITLE;
@@ -64,67 +61,6 @@ public class ManagedDeviceActionDisabledByAdminControllerTest {
}
@Test
- public void setupLearnMoreButton_noUrl_negativeButtonSet() {
- ManagedDeviceActionDisabledByAdminController controller = createController(EMPTY_URL);
-
- controller.setupLearnMoreButton(mContext);
-
- mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES);
- }
-
- @Test
- public void setupLearnMoreButton_validUrl_foregroundUser_launchesHelpPage() {
- ManagedDeviceActionDisabledByAdminController controller = createController(
- URL,
- /* isUserForeground= */ true,
- /* preferredUserHandle= */ MANAGED_USER,
- /* userContainingBrowser= */ MANAGED_USER);
-
- controller.setupLearnMoreButton(mContext);
-
- mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_LAUNCH_HELP_PAGE);
- }
-
- @Test
- public void setupLearnMoreButton_validUrl_browserInPreferredUser_notForeground_showsAdminPolicies() {
- ManagedDeviceActionDisabledByAdminController controller = createController(
- URL,
- /* isUserForeground= */ false,
- /* preferredUserHandle= */ MANAGED_USER,
- /* userContainingBrowser= */ MANAGED_USER);
-
- controller.setupLearnMoreButton(mContext);
-
- mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES);
- }
-
- @Test
- public void setupLearnMoreButton_validUrl_browserInCurrentUser_launchesHelpPage() {
- ManagedDeviceActionDisabledByAdminController controller = createController(
- URL,
- /* isUserForeground= */ false,
- /* preferredUserHandle= */ MANAGED_USER,
- /* userContainingBrowser= */ mContext.getUser());
-
- controller.setupLearnMoreButton(mContext);
-
- mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_LAUNCH_HELP_PAGE);
- }
-
- @Test
- public void setupLearnMoreButton_validUrl_browserNotOnAnyUser_showsAdminPolicies() {
- ManagedDeviceActionDisabledByAdminController controller = createController(
- URL,
- /* isUserForeground= */ false,
- /* preferredUserHandle= */ MANAGED_USER,
- /* userContainingBrowser= */ null);
-
- controller.setupLearnMoreButton(mContext);
-
- mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES);
- }
-
- @Test
public void getAdminSupportTitleResource_noRestriction_works() {
ManagedDeviceActionDisabledByAdminController controller = createController();
@@ -137,7 +73,7 @@ public class ManagedDeviceActionDisabledByAdminControllerTest {
ManagedDeviceActionDisabledByAdminController controller = createController();
assertThat(controller.getAdminSupportTitle(RESTRICTION))
- .isEqualTo(SUPPORT_TITLE_FOR_RESTRICTION);
+ .isEqualTo(DEFAULT_DISABLED_BY_POLICY_TITLE);
}
@Test
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 5e0d93548dc3..753e11099717 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -221,7 +221,7 @@ android_library {
"metrics-helper-lib",
"hamcrest-library",
"androidx.test.rules",
- "androidx.test.uiautomator",
+ "androidx.test.uiautomator_uiautomator",
"mockito-target-extended-minus-junit4",
"testables",
"truth-prebuilt",
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 3325eec0c451..6d61fd86e39d 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -5,20 +5,32 @@ set noparent
dsandler@android.com
aaliomer@google.com
+aaronjli@google.com
+acul@google.com
adamcohen@google.com
+aioana@google.com
alexflo@google.com
+andonian@google.com
+aroederer@google.com
arteiro@google.com
asc@google.com
awickham@google.com
+ayepin@google.com
+bbade@google.com
beverlyt@google.com
-brzezinski@google.com
+bhinegardner@google.com
+bhnm@google.com
brycelee@google.com
+brzezinski@google.com
caitlinshk@google.com
+chandruis@google.com
chrisgollner@google.com
+cinek@google.com
dupin@google.com
ethibodeau@google.com
evanlaird@google.com
florenceyang@google.com
+gallmann@google.com
gwasserman@google.com
hwwang@google.com
hyunyoungs@google.com
@@ -35,31 +47,41 @@ joshtrask@google.com
juliacr@google.com
juliatuttle@google.com
justinkoh@google.com
+justinweir@google.com
kozynski@google.com
kprevas@google.com
+lusilva@google.com
lynhan@google.com
madym@google.com
mankoff@google.com
+mateuszc@google.com
+michaelmikhil@google.com
+michschn@google.com
mkephart@google.com
mpietal@google.com
mrcasey@google.com
mrenouf@google.com
nickchameyev@google.com
nicomazz@google.com
+nijamkin@google.com
ogunwale@google.com
+omarmt@google.com
+patmanning@google.com
peanutbutter@google.com
peskal@google.com
pinyaoting@google.com
pixel@google.com
pomini@google.com
rahulbanerjee@google.com
+rasheedlewis@google.com
roosa@google.com
+saff@google.com
santie@google.com
shanh@google.com
snoeberger@google.com
steell@google.com
-sfufa@google.com
stwu@google.com
+syeonlee@google.com
sunnygoyal@google.com
susikp@google.com
thiruram@google.com
@@ -70,10 +92,13 @@ vadimt@google.com
victortulias@google.com
winsonc@google.com
wleshner@google.com
+xilei@google.com
xuqiu@google.com
+yeinj@google.com
yuandizhou@google.com
yurilin@google.com
zakcohen@google.com
+zoepage@google.com
#Android TV
rgl@google.com
diff --git a/packages/SystemUI/animation/res/values/ids.xml b/packages/SystemUI/animation/res/values/ids.xml
index f7150ab548dd..2d82307aca76 100644
--- a/packages/SystemUI/animation/res/values/ids.xml
+++ b/packages/SystemUI/animation/res/values/ids.xml
@@ -16,7 +16,6 @@
-->
<resources>
<!-- DialogLaunchAnimator -->
- <item type="id" name="tag_launch_animation_running"/>
<item type="id" name="tag_dialog_background"/>
<!-- ViewBoundsAnimator -->
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 9656b8a99d41..23cee4d0972d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -29,12 +29,12 @@ import android.view.GhostView
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewRootImpl
import android.view.WindowInsets
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
import android.widget.FrameLayout
import com.android.internal.jank.InteractionJankMonitor
-import com.android.internal.jank.InteractionJankMonitor.Configuration
import com.android.internal.jank.InteractionJankMonitor.CujType
import kotlin.math.roundToInt
@@ -46,6 +46,7 @@ private const val TAG = "DialogLaunchAnimator"
*
* This animator also allows to easily animate a dialog into an activity.
*
+ * @see show
* @see showFromView
* @see showFromDialog
* @see createActivityLaunchController
@@ -67,8 +68,81 @@ constructor(
ActivityLaunchAnimator.INTERPOLATORS.copy(
positionXInterpolator = ActivityLaunchAnimator.INTERPOLATORS.positionInterpolator
)
+ }
+
+ /**
+ * A controller that takes care of applying the dialog launch and exit animations to the source
+ * that triggered the animation.
+ */
+ interface Controller {
+ /** The [ViewRootImpl] of this controller. */
+ val viewRoot: ViewRootImpl
+
+ /**
+ * The identity object of the source animated by this controller. This animator will ensure
+ * that 2 animations with the same source identity are not going to run at the same time, to
+ * avoid flickers when a dialog is shown from the same source more or less at the same time
+ * (for instance if the user clicks an expandable button twice).
+ */
+ val sourceIdentity: Any
+
+ /**
+ * Move the drawing of the source in the overlay of [viewGroup].
+ *
+ * Once this method is called, and until [stopDrawingInOverlay] is called, the source
+ * controlled by this Controller should be drawn in the overlay of [viewGroup] so that it is
+ * drawn above all other elements in the same [viewRoot].
+ */
+ fun startDrawingInOverlayOf(viewGroup: ViewGroup)
+
+ /**
+ * Move the drawing of the source back in its original location.
+ *
+ * @see startDrawingInOverlayOf
+ */
+ fun stopDrawingInOverlay()
+
+ /**
+ * Create the [LaunchAnimator.Controller] that will be called to animate the source
+ * controlled by this [Controller] during the dialog launch animation.
+ *
+ * At the end of this animation, the source should *not* be visible anymore (until the
+ * dialog is closed and is animated back into the source).
+ */
+ fun createLaunchController(): LaunchAnimator.Controller
+
+ /**
+ * Create the [LaunchAnimator.Controller] that will be called to animate the source
+ * controlled by this [Controller] during the dialog exit animation.
+ *
+ * At the end of this animation, the source should be visible again.
+ */
+ fun createExitController(): LaunchAnimator.Controller
+
+ /**
+ * Whether we should animate the dialog back into the source when it is dismissed. If this
+ * methods returns `false`, then the dialog will simply fade out and
+ * [onExitAnimationCancelled] will be called.
+ *
+ * Note that even when this returns `true`, the exit animation might still be cancelled (in
+ * which case [onExitAnimationCancelled] will also be called).
+ */
+ fun shouldAnimateExit(): Boolean
- private val TAG_LAUNCH_ANIMATION_RUNNING = R.id.tag_launch_animation_running
+ /**
+ * Called if we decided to *not* animate the dialog into the source for some reason. This
+ * means that [createExitController] will *not* be called and this implementation should
+ * make sure that the source is back in its original state, before it was animated into the
+ * dialog. In particular, the source should be visible again.
+ */
+ fun onExitAnimationCancelled()
+
+ /**
+ * Return the [InteractionJankMonitor.Configuration.Builder] to be used for animations
+ * controlled by this controller.
+ */
+ // TODO(b/252723237): Make this non-nullable
+ fun jankConfigurationBuilder(cuj: Int): InteractionJankMonitor.Configuration.Builder?
}
/**
@@ -96,7 +170,28 @@ constructor(
dialog: Dialog,
view: View,
cuj: DialogCuj? = null,
- animateBackgroundBoundsChange: Boolean = false,
+ animateBackgroundBoundsChange: Boolean = false
+ ) {
+ show(dialog, createController(view), cuj, animateBackgroundBoundsChange)
+ }
+
+ /**
+ * Show [dialog] by expanding it from a source controlled by [controller].
+ *
+ * If [animateBackgroundBoundsChange] is true, then the background of the dialog will be
+ * animated when the dialog bounds change.
+ *
+ * Note: The background of [view] should be a (rounded) rectangle so that it can be properly
+ * animated.
+ *
+ * Caveats: When calling this function and [dialog] is not a fullscreen dialog, then it will be
+ * made fullscreen and 2 views will be inserted between the dialog DecorView and its children.
+ */
+ fun show(
+ dialog: Dialog,
+ controller: Controller,
+ cuj: DialogCuj? = null,
+ animateBackgroundBoundsChange: Boolean = false
) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw IllegalStateException(
@@ -109,9 +204,10 @@ constructor(
// intent is to launch a dialog from another dialog.
val animatedParent =
openedDialogs.firstOrNull {
- it.dialog.window.decorView.viewRootImpl == view.viewRootImpl
+ it.dialog.window.decorView.viewRootImpl == controller.viewRoot
}
- val animateFrom = animatedParent?.dialogContentWithBackground ?: view
+ val animateFrom =
+ animatedParent?.dialogContentWithBackground?.let { createController(it) } ?: controller
if (animatedParent == null && animateFrom !is LaunchableView) {
// Make sure the View we launch from implements LaunchableView to avoid visibility
@@ -126,15 +222,17 @@ constructor(
)
}
- // Make sure we don't run the launch animation from the same view twice at the same time.
- if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
- Log.e(TAG, "Not running dialog launch animation as there is already one running")
+ // Make sure we don't run the launch animation from the same source twice at the same time.
+ if (openedDialogs.any { it.controller.sourceIdentity == controller.sourceIdentity }) {
+ Log.e(
+ TAG,
+ "Not running dialog launch animation from source as it is already expanded into a" +
+ " dialog"
+ )
dialog.show()
return
}
- animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true)
-
val animatedDialog =
AnimatedDialog(
launchAnimator,
@@ -146,16 +244,99 @@ constructor(
animateBackgroundBoundsChange,
animatedParent,
isForTesting,
- cuj
+ cuj,
)
openedDialogs.add(animatedDialog)
animatedDialog.start()
}
+ /** Create a [Controller] that can animate [source] to & from a dialog. */
+ private fun createController(source: View): Controller {
+ return object : Controller {
+ override val viewRoot: ViewRootImpl
+ get() = source.viewRootImpl
+
+ override val sourceIdentity: Any = source
+
+ override fun startDrawingInOverlayOf(viewGroup: ViewGroup) {
+ // Create a temporary ghost of the source (which will make it invisible) and add it
+ // to the host dialog.
+ GhostView.addGhost(source, viewGroup)
+
+ // The ghost of the source was just created, so the source is currently invisible.
+ // We need to make sure that it stays invisible as long as the dialog is shown or
+ // animating.
+ (source as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
+ }
+
+ override fun stopDrawingInOverlay() {
+ // Note: here we should remove the ghost from the overlay, but in practice this is
+ // already done by the launch controllers created below.
+
+ // Make sure we allow the source to change its visibility again.
+ (source as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
+ source.visibility = View.VISIBLE
+ }
+
+ override fun createLaunchController(): LaunchAnimator.Controller {
+ val delegate = GhostedViewLaunchAnimatorController(source)
+ return object : LaunchAnimator.Controller by delegate {
+ override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+ // Remove the temporary ghost added by [startDrawingInOverlayOf]. Another
+ // ghost (that ghosts only the source content, and not its background) will
+ // be added right after this by the delegate and will be animated.
+ GhostView.removeGhost(source)
+ delegate.onLaunchAnimationStart(isExpandingFullyAbove)
+ }
+
+ override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
+
+ // We hide the source when the dialog is showing. We will make this view
+ // visible again when dismissing the dialog. This does nothing if the source
+ // implements [LaunchableView], as it's already INVISIBLE in that case.
+ source.visibility = View.INVISIBLE
+ }
+ }
+ }
+
+ override fun createExitController(): LaunchAnimator.Controller {
+ return GhostedViewLaunchAnimatorController(source)
+ }
+
+ override fun shouldAnimateExit(): Boolean {
+ // The source should be invisible by now, if it's not then something else changed
+ // its visibility and we probably don't want to run the animation.
+ if (source.visibility != View.INVISIBLE) {
+ return false
+ }
+
+ return source.isAttachedToWindow && ((source.parent as? View)?.isShown ?: true)
+ }
+
+ override fun onExitAnimationCancelled() {
+ // Make sure we allow the source to change its visibility again.
+ (source as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
+
+ // If the view is invisible it's probably because of us, so we make it visible
+ // again.
+ if (source.visibility == View.INVISIBLE) {
+ source.visibility = View.VISIBLE
+ }
+ }
+
+ override fun jankConfigurationBuilder(
+ cuj: Int
+ ): InteractionJankMonitor.Configuration.Builder? {
+ return InteractionJankMonitor.Configuration.Builder.withView(cuj, source)
+ }
+ }
+ }
+
/**
- * Launch [dialog] from [another dialog][animateFrom] that was shown using [showFromView]. This
- * will allow for dismissing the whole stack.
+ * Launch [dialog] from [another dialog][animateFrom] that was shown using [show]. This will
+ * allow for dismissing the whole stack.
*
* @see dismissStack
*/
@@ -181,32 +362,55 @@ constructor(
/**
* Create an [ActivityLaunchAnimator.Controller] that can be used to launch an activity from the
- * dialog that contains [View]. Note that the dialog must have been show using [showFromView]
- * and be currently showing, otherwise this will return null.
+ * dialog that contains [View]. Note that the dialog must have been shown using this animator,
+ * otherwise this method will return null.
*
* The returned controller will take care of dismissing the dialog at the right time after the
* activity started, when the dialog to app animation is done (or when it is cancelled). If this
* method returns null, then the dialog won't be dismissed.
*
- * Note: The background of [view] should be a (rounded) rectangle so that it can be properly
- * animated.
- *
* @param view any view inside the dialog to animate.
*/
@JvmOverloads
fun createActivityLaunchController(
view: View,
- cujType: Int? = null
+ cujType: Int? = null,
): ActivityLaunchAnimator.Controller? {
val animatedDialog =
openedDialogs.firstOrNull {
it.dialog.window.decorView.viewRootImpl == view.viewRootImpl
}
?: return null
+ return createActivityLaunchController(animatedDialog, cujType)
+ }
+ /**
+ * Create an [ActivityLaunchAnimator.Controller] that can be used to launch an activity from
+ * [dialog]. Note that the dialog must have been shown using this animator, otherwise this
+ * method will return null.
+ *
+ * The returned controller will take care of dismissing the dialog at the right time after the
+ * activity started, when the dialog to app animation is done (or when it is cancelled). If this
+ * method returns null, then the dialog won't be dismissed.
+ *
+ * @param dialog the dialog to animate.
+ */
+ @JvmOverloads
+ fun createActivityLaunchController(
+ dialog: Dialog,
+ cujType: Int? = null,
+ ): ActivityLaunchAnimator.Controller? {
+ val animatedDialog = openedDialogs.firstOrNull { it.dialog == dialog } ?: return null
+ return createActivityLaunchController(animatedDialog, cujType)
+ }
+
+ private fun createActivityLaunchController(
+ animatedDialog: AnimatedDialog,
+ cujType: Int? = null
+ ): ActivityLaunchAnimator.Controller? {
// At this point, we know that the intent of the caller is to dismiss the dialog to show
- // an app, so we disable the exit animation into the touch surface because we will never
- // want to run it anyways.
+ // an app, so we disable the exit animation into the source because we will never want to
+ // run it anyways.
animatedDialog.exitAnimationDisabled = true
val dialog = animatedDialog.dialog
@@ -252,7 +456,7 @@ constructor(
// If this dialog was shown from a cascade of other dialogs, make sure those ones
// are dismissed too.
- animatedDialog.touchSurface = animatedDialog.prepareForStackDismiss()
+ animatedDialog.prepareForStackDismiss()
// Remove the dim.
dialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
@@ -283,12 +487,11 @@ constructor(
}
/**
- * Ensure that all dialogs currently shown won't animate into their touch surface when
- * dismissed.
+ * Ensure that all dialogs currently shown won't animate into their source when dismissed.
*
* This is a temporary API meant to be called right before we both dismiss a dialog and start an
- * activity, which currently does not look good if we animate the dialog into the touch surface
- * at the same time as the activity starts.
+ * activity, which currently does not look good if we animate the dialog into their source at
+ * the same time as the activity starts.
*
* TODO(b/193634619): Remove this function and animate dialog into opening activity instead.
*/
@@ -297,13 +500,11 @@ constructor(
}
/**
- * Dismiss [dialog]. If it was launched from another dialog using [showFromView], also dismiss
- * the stack of dialogs, animating back to the original touchSurface.
+ * Dismiss [dialog]. If it was launched from another dialog using this animator, also dismiss
+ * the stack of dialogs and simply fade out [dialog].
*/
fun dismissStack(dialog: Dialog) {
- openedDialogs
- .firstOrNull { it.dialog == dialog }
- ?.let { it.touchSurface = it.prepareForStackDismiss() }
+ openedDialogs.firstOrNull { it.dialog == dialog }?.prepareForStackDismiss()
dialog.dismiss()
}
@@ -337,8 +538,11 @@ private class AnimatedDialog(
private val callback: DialogLaunchAnimator.Callback,
private val interactionJankMonitor: InteractionJankMonitor,
- /** The view that triggered the dialog after being tapped. */
- var touchSurface: View,
+ /**
+ * The controller of the source that triggered the dialog and that will animate into/from the
+ * dialog.
+ */
+ val controller: DialogLaunchAnimator.Controller,
/**
* A callback that will be called with this [AnimatedDialog] after the dialog was dismissed and
@@ -383,17 +587,18 @@ private class AnimatedDialog(
private var originalDialogBackgroundColor = Color.BLACK
/**
- * Whether we are currently launching/showing the dialog by animating it from [touchSurface].
+ * Whether we are currently launching/showing the dialog by animating it from its source
+ * controlled by [controller].
*/
private var isLaunching = true
- /** Whether we are currently dismissing/hiding the dialog by animating into [touchSurface]. */
+ /** Whether we are currently dismissing/hiding the dialog by animating into its source. */
private var isDismissing = false
private var dismissRequested = false
var exitAnimationDisabled = false
- private var isTouchSurfaceGhostDrawn = false
+ private var isSourceDrawnInDialog = false
private var isOriginalDialogViewLaidOut = false
/** A layout listener to animate the dialog height change. */
@@ -410,13 +615,19 @@ private class AnimatedDialog(
*/
private var decorViewLayoutListener: View.OnLayoutChangeListener? = null
+ private var hasInstrumentedJank = false
+
fun start() {
if (cuj != null) {
- val config = Configuration.Builder.withView(cuj.cujType, touchSurface)
- if (cuj.tag != null) {
- config.setTag(cuj.tag)
+ val config = controller.jankConfigurationBuilder(cuj.cujType)
+ if (config != null) {
+ if (cuj.tag != null) {
+ config.setTag(cuj.tag)
+ }
+
+ interactionJankMonitor.begin(config)
+ hasInstrumentedJank = true
}
- interactionJankMonitor.begin(config)
}
// Create the dialog so that its onCreate() method is called, which usually sets the dialog
@@ -618,47 +829,45 @@ private class AnimatedDialog(
// Show the dialog.
dialog.show()
- addTouchSurfaceGhost()
+ moveSourceDrawingToDialog()
}
- private fun addTouchSurfaceGhost() {
+ private fun moveSourceDrawingToDialog() {
if (decorView.viewRootImpl == null) {
- // Make sure that we have access to the dialog view root to synchronize the creation of
- // the ghost.
- decorView.post(::addTouchSurfaceGhost)
+ // Make sure that we have access to the dialog view root to move the drawing to the
+ // dialog overlay.
+ decorView.post(::moveSourceDrawingToDialog)
return
}
- // Create a ghost of the touch surface (which will make the touch surface invisible) and add
- // it to the host dialog. We trigger a one off synchronization to make sure that this is
- // done in sync between the two different windows.
+ // Move the drawing of the source in the overlay of this dialog, then animate. We trigger a
+ // one-off synchronization to make sure that this is done in sync between the two different
+ // windows.
synchronizeNextDraw(
then = {
- isTouchSurfaceGhostDrawn = true
+ isSourceDrawnInDialog = true
maybeStartLaunchAnimation()
}
)
- GhostView.addGhost(touchSurface, decorView)
-
- // The ghost of the touch surface was just created, so the touch surface is currently
- // invisible. We need to make sure that it stays invisible as long as the dialog is shown or
- // animating.
- (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
+ controller.startDrawingInOverlayOf(decorView)
}
/**
- * Synchronize the next draw of the touch surface and dialog view roots so that they are
- * performed at the same time, in the same transaction. This is necessary to make sure that the
- * ghost of the touch surface is drawn at the same time as the touch surface is made invisible
- * (or inversely, removed from the UI when the touch surface is made visible).
+ * Synchronize the next draw of the source and dialog view roots so that they are performed at
+ * the same time, in the same transaction. This is necessary to make sure that the source is
+ * drawn in the overlay at the same time as it is removed from its original position (or
+ * inversely, removed from the overlay when the source is moved back to its original position).
*/
private fun synchronizeNextDraw(then: () -> Unit) {
if (forceDisableSynchronization) {
+ // Don't synchronize when inside an automated test.
then()
return
}
- ViewRootSync.synchronizeNextDraw(touchSurface, decorView, then)
+ ViewRootSync.synchronizeNextDraw(decorView, controller.viewRoot.view, then)
+ decorView.invalidate()
+ controller.viewRoot.view.invalidate()
}
private fun findFirstViewGroupWithBackground(view: View): ViewGroup? {
@@ -681,7 +890,7 @@ private class AnimatedDialog(
}
private fun maybeStartLaunchAnimation() {
- if (!isTouchSurfaceGhostDrawn || !isOriginalDialogViewLaidOut) {
+ if (!isSourceDrawnInDialog || !isOriginalDialogViewLaidOut) {
return
}
@@ -690,19 +899,7 @@ private class AnimatedDialog(
startAnimation(
isLaunching = true,
- onLaunchAnimationStart = {
- // Remove the temporary ghost. Another ghost (that ghosts only the touch surface
- // content, and not its background) will be added right after this and will be
- // animated.
- GhostView.removeGhost(touchSurface)
- },
onLaunchAnimationEnd = {
- touchSurface.setTag(R.id.tag_launch_animation_running, null)
-
- // We hide the touch surface when the dialog is showing. We will make this view
- // visible again when dismissing the dialog.
- touchSurface.visibility = View.INVISIBLE
-
isLaunching = false
// dismiss was called during the animation, dismiss again now to actually dismiss.
@@ -718,7 +915,10 @@ private class AnimatedDialog(
backgroundLayoutListener
)
}
- cuj?.run { interactionJankMonitor.end(cujType) }
+
+ if (hasInstrumentedJank) {
+ interactionJankMonitor.end(cuj!!.cujType)
+ }
}
)
}
@@ -753,8 +953,8 @@ private class AnimatedDialog(
}
/**
- * Hide the dialog into the touch surface and call [onAnimationFinished] when the animation is
- * done (passing animationRan=true) or if it's skipped (passing animationRan=false) to actually
+ * Hide the dialog into the source and call [onAnimationFinished] when the animation is done
+ * (passing animationRan=true) or if it's skipped (passing animationRan=false) to actually
* dismiss the dialog.
*/
private fun hideDialogIntoView(onAnimationFinished: (Boolean) -> Unit) {
@@ -763,17 +963,9 @@ private class AnimatedDialog(
decorView.removeOnLayoutChangeListener(decorViewLayoutListener)
}
- if (!shouldAnimateDialogIntoView()) {
- Log.i(TAG, "Skipping animation of dialog into the touch surface")
-
- // Make sure we allow the touch surface to change its visibility again.
- (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
-
- // If the view is invisible it's probably because of us, so we make it visible again.
- if (touchSurface.visibility == View.INVISIBLE) {
- touchSurface.visibility = View.VISIBLE
- }
-
+ if (!shouldAnimateDialogIntoSource()) {
+ Log.i(TAG, "Skipping animation of dialog into the source")
+ controller.onExitAnimationCancelled()
onAnimationFinished(false /* instantDismiss */)
onDialogDismissed(this@AnimatedDialog)
return
@@ -786,10 +978,6 @@ private class AnimatedDialog(
dialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
},
onLaunchAnimationEnd = {
- // Make sure we allow the touch surface to change its visibility again.
- (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
-
- touchSurface.visibility = View.VISIBLE
val dialogContentWithBackground = this.dialogContentWithBackground!!
dialogContentWithBackground.visibility = View.INVISIBLE
@@ -799,14 +987,11 @@ private class AnimatedDialog(
)
}
- // Make sure that the removal of the ghost and making the touch surface visible is
- // done at the same time.
- synchronizeNextDraw(
- then = {
- onAnimationFinished(true /* instantDismiss */)
- onDialogDismissed(this@AnimatedDialog)
- }
- )
+ controller.stopDrawingInOverlay()
+ synchronizeNextDraw {
+ onAnimationFinished(true /* instantDismiss */)
+ onDialogDismissed(this@AnimatedDialog)
+ }
}
)
}
@@ -816,27 +1001,34 @@ private class AnimatedDialog(
onLaunchAnimationStart: () -> Unit = {},
onLaunchAnimationEnd: () -> Unit = {}
) {
- // Create 2 ghost controllers to animate both the dialog and the touch surface in the
- // dialog.
- val startView = if (isLaunching) touchSurface else dialogContentWithBackground!!
- val endView = if (isLaunching) dialogContentWithBackground!! else touchSurface
- val startViewController = GhostedViewLaunchAnimatorController(startView)
- val endViewController = GhostedViewLaunchAnimatorController(endView)
- startViewController.launchContainer = decorView
- endViewController.launchContainer = decorView
-
- val endState = endViewController.createAnimatorState()
+ // Create 2 controllers to animate both the dialog and the source.
+ val startController =
+ if (isLaunching) {
+ controller.createLaunchController()
+ } else {
+ GhostedViewLaunchAnimatorController(dialogContentWithBackground!!)
+ }
+ val endController =
+ if (isLaunching) {
+ GhostedViewLaunchAnimatorController(dialogContentWithBackground!!)
+ } else {
+ controller.createExitController()
+ }
+ startController.launchContainer = decorView
+ endController.launchContainer = decorView
+
+ val endState = endController.createAnimatorState()
val controller =
object : LaunchAnimator.Controller {
override var launchContainer: ViewGroup
- get() = startViewController.launchContainer
+ get() = startController.launchContainer
set(value) {
- startViewController.launchContainer = value
- endViewController.launchContainer = value
+ startController.launchContainer = value
+ endController.launchContainer = value
}
override fun createAnimatorState(): LaunchAnimator.State {
- return startViewController.createAnimatorState()
+ return startController.createAnimatorState()
}
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
@@ -845,15 +1037,29 @@ private class AnimatedDialog(
// onLaunchAnimationStart on the controller (which will create its own ghost).
onLaunchAnimationStart()
- startViewController.onLaunchAnimationStart(isExpandingFullyAbove)
- endViewController.onLaunchAnimationStart(isExpandingFullyAbove)
+ startController.onLaunchAnimationStart(isExpandingFullyAbove)
+ endController.onLaunchAnimationStart(isExpandingFullyAbove)
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
- startViewController.onLaunchAnimationEnd(isExpandingFullyAbove)
- endViewController.onLaunchAnimationEnd(isExpandingFullyAbove)
-
- onLaunchAnimationEnd()
+ // onLaunchAnimationEnd is called by an Animator at the end of the animation,
+ // on a Choreographer animation tick. The following calls will move the animated
+ // content from the dialog overlay back to its original position, and this
+ // change must be reflected in the next frame given that we then sync the next
+ // frame of both the content and dialog ViewRoots. However, in case that content
+ // is rendered by Compose, whose compositions are also scheduled on a
+ // Choreographer frame, any state change made *right now* won't be reflected in
+ // the next frame given that a Choreographer frame can't schedule another and
+ // have it happen in the same frame. So we post the forwarded calls to
+ // [Controller.onLaunchAnimationEnd], leaving this Choreographer frame, ensuring
+ // that the move of the content back to its original window will be reflected in
+ // the next frame right after [onLaunchAnimationEnd] is called.
+ dialog.context.mainExecutor.execute {
+ startController.onLaunchAnimationEnd(isExpandingFullyAbove)
+ endController.onLaunchAnimationEnd(isExpandingFullyAbove)
+
+ onLaunchAnimationEnd()
+ }
}
override fun onLaunchAnimationProgress(
@@ -861,11 +1067,11 @@ private class AnimatedDialog(
progress: Float,
linearProgress: Float
) {
- startViewController.onLaunchAnimationProgress(state, progress, linearProgress)
+ startController.onLaunchAnimationProgress(state, progress, linearProgress)
// The end view is visible only iff the starting view is not visible.
state.visible = !state.visible
- endViewController.onLaunchAnimationProgress(state, progress, linearProgress)
+ endController.onLaunchAnimationProgress(state, progress, linearProgress)
// If the dialog content is complex, its dimension might change during the
// launch animation. The animation end position might also change during the
@@ -873,14 +1079,16 @@ private class AnimatedDialog(
// Therefore we update the end state to the new position/size. Usually the
// dialog dimension or position will change in the early frames, so changing the
// end state shouldn't really be noticeable.
- endViewController.fillGhostedViewState(endState)
+ if (endController is GhostedViewLaunchAnimatorController) {
+ endController.fillGhostedViewState(endState)
+ }
}
}
launchAnimator.startAnimation(controller, endState, originalDialogBackgroundColor)
}
- private fun shouldAnimateDialogIntoView(): Boolean {
+ private fun shouldAnimateDialogIntoSource(): Boolean {
// Don't animate if the dialog was previously hidden using hide() or if we disabled the exit
// animation.
if (exitAnimationDisabled || !dialog.isShowing) {
@@ -888,24 +1096,12 @@ private class AnimatedDialog(
}
// If we are dreaming, the dialog was probably closed because of that so we don't animate
- // into the touchSurface.
+ // into the source.
if (callback.isDreaming()) {
return false
}
- // The touch surface should be invisible by now, if it's not then something else changed its
- // visibility and we probably don't want to run the animation.
- if (touchSurface.visibility != View.INVISIBLE) {
- return false
- }
-
- // If the touch surface is not attached or one of its ancestors is not visible, then we
- // don't run the animation either.
- if (!touchSurface.isAttachedToWindow) {
- return false
- }
-
- return (touchSurface.parent as? View)?.isShown ?: true
+ return controller.shouldAnimateExit()
}
/** A layout listener to animate the change of bounds of the dialog background. */
@@ -988,17 +1184,13 @@ private class AnimatedDialog(
}
}
- fun prepareForStackDismiss(): View {
+ fun prepareForStackDismiss() {
if (parentAnimatedDialog == null) {
- return touchSurface
+ return
}
parentAnimatedDialog.exitAnimationDisabled = true
parentAnimatedDialog.dialog.hide()
- val view = parentAnimatedDialog.prepareForStackDismiss()
+ parentAnimatedDialog.prepareForStackDismiss()
parentAnimatedDialog.dialog.dismiss()
- // Make the touch surface invisible, so we end up animating to it when we actually
- // dismiss the stack
- view.visibility = View.INVISIBLE
- return view
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index eb000ad312d7..0028d13ffd5e 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -199,6 +199,10 @@ open class GhostedViewLaunchAnimatorController @JvmOverloads constructor(
// the content before fading out the background.
ghostView = GhostView.addGhost(ghostedView, launchContainer)
+ // The ghost was just created, so ghostedView is currently invisible. We need to make sure
+ // that it stays invisible as long as we are animating.
+ (ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
+
val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
matrix.getValues(initialGhostViewMatrixValues)
@@ -293,6 +297,7 @@ open class GhostedViewLaunchAnimatorController @JvmOverloads constructor(
backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
GhostView.removeGhost(ghostedView)
+ (ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
launchContainerOverlay.remove(backgroundView)
// Make sure that the view is considered VISIBLE by accessibility by first making it
diff --git a/packages/SystemUI/compose/core/Android.bp b/packages/SystemUI/compose/core/Android.bp
index 4cfe39225a9b..fbdb526d6b31 100644
--- a/packages/SystemUI/compose/core/Android.bp
+++ b/packages/SystemUI/compose/core/Android.bp
@@ -30,8 +30,11 @@ android_library {
],
static_libs: [
+ "SystemUIAnimationLib",
+
"androidx.compose.runtime_runtime",
"androidx.compose.material3_material3",
+ "androidx.savedstate_savedstate",
],
kotlincflags: ["-Xjvm-default=all"],
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt
new file mode 100644
index 000000000000..edbd68400f83
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2022 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.compose.animation
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroupOverlay
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.contentColorFor
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCompositionContext
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.drawOutline
+import androidx.compose.ui.graphics.drawscope.scale
+import androidx.compose.ui.layout.boundsInRoot
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.Density
+import androidx.lifecycle.ViewTreeLifecycleOwner
+import androidx.lifecycle.ViewTreeViewModelStoreOwner
+import androidx.savedstate.findViewTreeSavedStateRegistryOwner
+import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import com.android.systemui.animation.LaunchAnimator
+import kotlin.math.min
+
+/**
+ * Create an expandable shape that can launch into an Activity or a Dialog.
+ *
+ * Example:
+ * ```
+ * Expandable(
+ * color = MaterialTheme.colorScheme.primary,
+ * shape = RoundedCornerShape(16.dp),
+ * ) { controller ->
+ * Row(
+ * Modifier
+ * // For activities:
+ * .clickable { activityStarter.startActivity(intent, controller.forActivity()) }
+ *
+ * // For dialogs:
+ * .clickable { dialogLaunchAnimator.show(dialog, controller.forDialog()) }
+ * ) { ... }
+ * }
+ * ```
+ *
+ * @sample com.android.systemui.compose.gallery.ActivityLaunchScreen
+ * @sample com.android.systemui.compose.gallery.DialogLaunchScreen
+ */
+@Composable
+fun Expandable(
+ color: Color,
+ shape: Shape,
+ modifier: Modifier = Modifier,
+ contentColor: Color = contentColorFor(color),
+ content: @Composable (ExpandableController) -> Unit,
+) {
+ Expandable(
+ rememberExpandableController(color, shape, contentColor),
+ modifier,
+ content,
+ )
+}
+
+/**
+ * Create an expandable shape that can launch into an Activity or a Dialog.
+ *
+ * This overload can be used in cases where you need to create the [ExpandableController] before
+ * composing this [Expandable], for instance if something outside of this Expandable can trigger a
+ * launch animation
+ *
+ * Example:
+ * ```
+ * // The controller that you can use to trigger the animations from anywhere.
+ * val controller =
+ * rememberExpandableController(
+ * color = MaterialTheme.colorScheme.primary,
+ * shape = RoundedCornerShape(16.dp),
+ * )
+ *
+ * Expandable(controller) {
+ * ...
+ * }
+ * ```
+ *
+ * @sample com.android.systemui.compose.gallery.ActivityLaunchScreen
+ * @sample com.android.systemui.compose.gallery.DialogLaunchScreen
+ */
+@Composable
+fun Expandable(
+ controller: ExpandableController,
+ modifier: Modifier = Modifier,
+ content: @Composable (ExpandableController) -> Unit,
+) {
+ val controller = controller as ExpandableControllerImpl
+ val color = controller.color
+ val contentColor = controller.contentColor
+ val shape = controller.shape
+
+ // TODO(b/230830644): Use movableContentOf to preserve the content state instead once the
+ // Compose libraries have been updated and include aosp/2163631.
+ val wrappedContent =
+ @Composable { controller: ExpandableController ->
+ CompositionLocalProvider(
+ LocalContentColor provides contentColor,
+ ) {
+ content(controller)
+ }
+ }
+
+ val thisExpandableSize by remember {
+ derivedStateOf { controller.boundsInComposeViewRoot.value.size }
+ }
+
+ // Make sure we don't read animatorState directly here to avoid recomposition every time the
+ // state changes (i.e. every frame of the animation).
+ val isAnimating by remember {
+ derivedStateOf {
+ controller.animatorState.value != null && controller.overlay.value != null
+ }
+ }
+
+ when {
+ isAnimating -> {
+ // Don't compose the movable content during the animation, as it should be composed only
+ // once at all times. We make this spacer exactly the same size as this Expandable when
+ // it is visible.
+ Spacer(
+ modifier
+ .clip(shape)
+ .requiredSize(with(controller.density) { thisExpandableSize.toDpSize() })
+ )
+
+ // The content and its animated background in the overlay. We draw it only when we are
+ // animating.
+ AnimatedContentInOverlay(
+ color,
+ thisExpandableSize,
+ controller.animatorState,
+ controller.overlay.value
+ ?: error("AnimatedContentInOverlay shouldn't be composed with null overlay."),
+ controller,
+ wrappedContent,
+ controller.composeViewRoot,
+ { controller.currentComposeViewInOverlay.value = it },
+ controller.density,
+ )
+ }
+ controller.isDialogShowing.value -> {
+ Box(
+ modifier
+ .drawWithContent { /* Don't draw anything when the dialog is shown. */}
+ .onGloballyPositioned {
+ controller.boundsInComposeViewRoot.value = it.boundsInRoot()
+ }
+ ) { wrappedContent(controller) }
+ }
+ else -> {
+ Box(
+ modifier.clip(shape).background(color, shape).onGloballyPositioned {
+ controller.boundsInComposeViewRoot.value = it.boundsInRoot()
+ }
+ ) { wrappedContent(controller) }
+ }
+ }
+}
+
+/** Draw [content] in [overlay] while respecting its screen position given by [animatorState]. */
+@Composable
+private fun AnimatedContentInOverlay(
+ color: Color,
+ sizeInOriginalLayout: Size,
+ animatorState: State<LaunchAnimator.State?>,
+ overlay: ViewGroupOverlay,
+ controller: ExpandableController,
+ content: @Composable (ExpandableController) -> Unit,
+ composeViewRoot: View,
+ onOverlayComposeViewChanged: (View?) -> Unit,
+ density: Density,
+) {
+ val compositionContext = rememberCompositionContext()
+ val context = LocalContext.current
+
+ // Create the ComposeView and force its content composition so that the movableContent is
+ // composed exactly once when we start animating.
+ val composeViewInOverlay =
+ remember(context, density) {
+ val startWidth = sizeInOriginalLayout.width
+ val startHeight = sizeInOriginalLayout.height
+ val contentModifier =
+ Modifier
+ // Draw the content with the same size as it was at the start of the animation
+ // so that its content is laid out exactly the same way.
+ .requiredSize(with(density) { sizeInOriginalLayout.toDpSize() })
+ .drawWithContent {
+ val animatorState = animatorState.value ?: return@drawWithContent
+
+ // Scale the content with the background while keeping its aspect ratio.
+ val widthRatio =
+ if (startWidth != 0f) {
+ animatorState.width.toFloat() / startWidth
+ } else {
+ 1f
+ }
+ val heightRatio =
+ if (startHeight != 0f) {
+ animatorState.height.toFloat() / startHeight
+ } else {
+ 1f
+ }
+ val scale = min(widthRatio, heightRatio)
+ scale(scale) { this@drawWithContent.drawContent() }
+ }
+
+ val composeView =
+ ComposeView(context).apply {
+ setContent {
+ Box(
+ Modifier.fillMaxSize().drawWithContent {
+ val animatorState = animatorState.value ?: return@drawWithContent
+ if (!animatorState.visible) {
+ return@drawWithContent
+ }
+
+ val topRadius = animatorState.topCornerRadius
+ val bottomRadius = animatorState.bottomCornerRadius
+ if (topRadius == bottomRadius) {
+ // Shortcut to avoid Outline calculation and allocation.
+ val cornerRadius = CornerRadius(topRadius)
+ drawRoundRect(color, cornerRadius = cornerRadius)
+ } else {
+ val shape =
+ RoundedCornerShape(
+ topStart = topRadius,
+ topEnd = topRadius,
+ bottomStart = bottomRadius,
+ bottomEnd = bottomRadius,
+ )
+ val outline = shape.createOutline(size, layoutDirection, this)
+ drawOutline(outline, color = color)
+ }
+
+ drawContent()
+ },
+ // We center the content in the expanding container.
+ contentAlignment = Alignment.Center,
+ ) {
+ Box(contentModifier) { content(controller) }
+ }
+ }
+ }
+
+ // Set the owners.
+ val overlayViewGroup =
+ getOverlayViewGroup(
+ context,
+ overlay,
+ )
+ ViewTreeLifecycleOwner.set(
+ overlayViewGroup,
+ ViewTreeLifecycleOwner.get(composeViewRoot),
+ )
+ ViewTreeViewModelStoreOwner.set(
+ overlayViewGroup,
+ ViewTreeViewModelStoreOwner.get(composeViewRoot),
+ )
+ overlayViewGroup.setViewTreeSavedStateRegistryOwner(
+ composeViewRoot.findViewTreeSavedStateRegistryOwner()
+ )
+
+ composeView.setParentCompositionContext(compositionContext)
+
+ composeView
+ }
+
+ DisposableEffect(overlay, composeViewInOverlay) {
+ // Add the ComposeView to the overlay.
+ overlay.add(composeViewInOverlay)
+
+ val startState =
+ animatorState.value
+ ?: throw IllegalStateException(
+ "AnimatedContentInOverlay shouldn't be composed with null animatorState."
+ )
+ measureAndLayoutComposeViewInOverlay(composeViewInOverlay, startState)
+ onOverlayComposeViewChanged(composeViewInOverlay)
+
+ onDispose {
+ composeViewInOverlay.disposeComposition()
+ overlay.remove(composeViewInOverlay)
+ onOverlayComposeViewChanged(null)
+ }
+ }
+}
+
+internal fun measureAndLayoutComposeViewInOverlay(
+ view: View,
+ state: LaunchAnimator.State,
+) {
+ val exactWidth = state.width
+ val exactHeight = state.height
+ view.measure(
+ View.MeasureSpec.makeSafeMeasureSpec(exactWidth, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeSafeMeasureSpec(exactHeight, View.MeasureSpec.EXACTLY),
+ )
+
+ val parent = view.parent as ViewGroup
+ val parentLocation = parent.locationOnScreen
+ val offsetX = parentLocation[0]
+ val offsetY = parentLocation[1]
+ view.layout(
+ state.left - offsetX,
+ state.top - offsetY,
+ state.right - offsetX,
+ state.bottom - offsetY,
+ )
+}
+
+// TODO(b/230830644): Add hidden API to ViewGroupOverlay to access this ViewGroup directly?
+private fun getOverlayViewGroup(context: Context, overlay: ViewGroupOverlay): ViewGroup {
+ val view = View(context)
+ overlay.add(view)
+ var current = view.parent
+ while (current.parent != null) {
+ current = current.parent
+ }
+ overlay.remove(view)
+ return current as ViewGroup
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
new file mode 100644
index 000000000000..065c3149c2f5
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2022 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.compose.animation
+
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroupOverlay
+import android.view.ViewRootImpl
+import androidx.compose.material3.contentColorFor
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.animation.LaunchAnimator
+import kotlin.math.roundToInt
+
+/** A controller that can control animated launches. */
+interface ExpandableController {
+ /** Create an [ActivityLaunchAnimator.Controller] to animate into an Activity. */
+ fun forActivity(): ActivityLaunchAnimator.Controller
+
+ /** Create a [DialogLaunchAnimator.Controller] to animate into a Dialog. */
+ fun forDialog(): DialogLaunchAnimator.Controller
+}
+
+/**
+ * Create an [ExpandableController] to control an [Expandable]. This is useful if you need to create
+ * the controller before the [Expandable], for instance to handle clicks outside of the Expandable
+ * that would still trigger a dialog/activity launch animation.
+ */
+@Composable
+fun rememberExpandableController(
+ color: Color,
+ shape: Shape,
+ contentColor: Color = contentColorFor(color),
+): ExpandableController {
+ val composeViewRoot = LocalView.current
+ val density = LocalDensity.current
+ val layoutDirection = LocalLayoutDirection.current
+
+ // The current animation state, if we are currently animating a dialog or activity.
+ val animatorState = remember { mutableStateOf<LaunchAnimator.State?>(null) }
+
+ // Whether a dialog controlled by this ExpandableController is currently showing.
+ val isDialogShowing = remember { mutableStateOf(false) }
+
+ // The overlay in which we should animate the launch.
+ val overlay = remember { mutableStateOf<ViewGroupOverlay?>(null) }
+
+ // The current [ComposeView] being animated in the [overlay], if any.
+ val currentComposeViewInOverlay = remember { mutableStateOf<View?>(null) }
+
+ // The bounds in [composeViewRoot] of the expandable controlled by this controller.
+ val boundsInComposeViewRoot = remember { mutableStateOf(Rect.Zero) }
+
+ // Whether this composable is still composed. We only do the dialog exit animation if this is
+ // true.
+ val isComposed = remember { mutableStateOf(true) }
+ DisposableEffect(Unit) { onDispose { isComposed.value = false } }
+
+ return remember(color, contentColor, shape, composeViewRoot, density, layoutDirection) {
+ ExpandableControllerImpl(
+ color,
+ contentColor,
+ shape,
+ composeViewRoot,
+ density,
+ animatorState,
+ isDialogShowing,
+ overlay,
+ currentComposeViewInOverlay,
+ boundsInComposeViewRoot,
+ layoutDirection,
+ isComposed,
+ )
+ }
+}
+
+internal class ExpandableControllerImpl(
+ internal val color: Color,
+ internal val contentColor: Color,
+ internal val shape: Shape,
+ internal val composeViewRoot: View,
+ internal val density: Density,
+ internal val animatorState: MutableState<LaunchAnimator.State?>,
+ internal val isDialogShowing: MutableState<Boolean>,
+ internal val overlay: MutableState<ViewGroupOverlay?>,
+ internal val currentComposeViewInOverlay: MutableState<View?>,
+ internal val boundsInComposeViewRoot: MutableState<Rect>,
+ private val layoutDirection: LayoutDirection,
+ private val isComposed: State<Boolean>,
+) : ExpandableController {
+ override fun forActivity(): ActivityLaunchAnimator.Controller {
+ return activityController()
+ }
+
+ override fun forDialog(): DialogLaunchAnimator.Controller {
+ return dialogController()
+ }
+
+ /**
+ * Create a [LaunchAnimator.Controller] that is going to be used to drive an activity or dialog
+ * animation. This controller will:
+ * 1. Compute the start/end animation state using [boundsInComposeViewRoot] and the location of
+ * composeViewRoot on the screen.
+ * 2. Update [animatorState] with the current animation state if we are animating, or null
+ * otherwise.
+ */
+ private fun launchController(): LaunchAnimator.Controller {
+ return object : LaunchAnimator.Controller {
+ private val rootLocationOnScreen = intArrayOf(0, 0)
+
+ override var launchContainer: ViewGroup = composeViewRoot.rootView as ViewGroup
+
+ override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ animatorState.value = null
+ }
+
+ override fun onLaunchAnimationProgress(
+ state: LaunchAnimator.State,
+ progress: Float,
+ linearProgress: Float
+ ) {
+ // We copy state given that it's always the same object that is mutated by
+ // ActivityLaunchAnimator.
+ animatorState.value =
+ LaunchAnimator.State(
+ state.top,
+ state.bottom,
+ state.left,
+ state.right,
+ state.topCornerRadius,
+ state.bottomCornerRadius,
+ )
+ .apply { visible = state.visible }
+
+ // Force measure and layout the ComposeView in the overlay whenever the animation
+ // state changes.
+ currentComposeViewInOverlay.value?.let {
+ measureAndLayoutComposeViewInOverlay(it, state)
+ }
+ }
+
+ override fun createAnimatorState(): LaunchAnimator.State {
+ val boundsInRoot = boundsInComposeViewRoot.value
+ val outline =
+ shape.createOutline(
+ Size(boundsInRoot.width, boundsInRoot.height),
+ layoutDirection,
+ density,
+ )
+
+ val (topCornerRadius, bottomCornerRadius) =
+ when (outline) {
+ is Outline.Rectangle -> 0f to 0f
+ is Outline.Rounded -> {
+ val roundRect = outline.roundRect
+
+ // TODO(b/230830644): Add better support different corner radii.
+ val topCornerRadius =
+ maxOf(
+ roundRect.topLeftCornerRadius.x,
+ roundRect.topLeftCornerRadius.y,
+ roundRect.topRightCornerRadius.x,
+ roundRect.topRightCornerRadius.y,
+ )
+ val bottomCornerRadius =
+ maxOf(
+ roundRect.bottomLeftCornerRadius.x,
+ roundRect.bottomLeftCornerRadius.y,
+ roundRect.bottomRightCornerRadius.x,
+ roundRect.bottomRightCornerRadius.y,
+ )
+
+ topCornerRadius to bottomCornerRadius
+ }
+ else ->
+ error(
+ "ExpandableState only supports (rounded) rectangles at the " +
+ "moment."
+ )
+ }
+
+ val rootLocation = rootLocationOnScreen()
+ return LaunchAnimator.State(
+ top = rootLocation.y.roundToInt(),
+ bottom = (rootLocation.y + boundsInRoot.height).roundToInt(),
+ left = rootLocation.x.roundToInt(),
+ right = (rootLocation.x + boundsInRoot.width).roundToInt(),
+ topCornerRadius = topCornerRadius,
+ bottomCornerRadius = bottomCornerRadius,
+ )
+ }
+
+ private fun rootLocationOnScreen(): Offset {
+ composeViewRoot.getLocationOnScreen(rootLocationOnScreen)
+ val boundsInRoot = boundsInComposeViewRoot.value
+ val x = rootLocationOnScreen[0] + boundsInRoot.left
+ val y = rootLocationOnScreen[1] + boundsInRoot.top
+ return Offset(x, y)
+ }
+ }
+ }
+
+ /** Create an [ActivityLaunchAnimator.Controller] that can be used to animate activities. */
+ private fun activityController(): ActivityLaunchAnimator.Controller {
+ val delegate = launchController()
+ return object : ActivityLaunchAnimator.Controller, LaunchAnimator.Controller by delegate {
+ override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+ delegate.onLaunchAnimationStart(isExpandingFullyAbove)
+ overlay.value = composeViewRoot.rootView.overlay as ViewGroupOverlay
+ }
+
+ override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
+ overlay.value = null
+ }
+ }
+ }
+
+ private fun dialogController(): DialogLaunchAnimator.Controller {
+ return object : DialogLaunchAnimator.Controller {
+ override val viewRoot: ViewRootImpl = composeViewRoot.viewRootImpl
+ override val sourceIdentity: Any = this@ExpandableControllerImpl
+
+ override fun startDrawingInOverlayOf(viewGroup: ViewGroup) {
+ val newOverlay = viewGroup.overlay as ViewGroupOverlay
+ if (newOverlay != overlay.value) {
+ overlay.value = newOverlay
+ }
+ }
+
+ override fun stopDrawingInOverlay() {
+ if (overlay.value != null) {
+ overlay.value = null
+ }
+ }
+
+ override fun createLaunchController(): LaunchAnimator.Controller {
+ val delegate = launchController()
+ return object : LaunchAnimator.Controller by delegate {
+ override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
+
+ // Make sure we don't draw this expandable when the dialog is showing.
+ isDialogShowing.value = true
+ }
+ }
+ }
+
+ override fun createExitController(): LaunchAnimator.Controller {
+ val delegate = launchController()
+ return object : LaunchAnimator.Controller by delegate {
+ override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
+ isDialogShowing.value = false
+ }
+ }
+ }
+
+ override fun shouldAnimateExit(): Boolean = isComposed.value
+
+ override fun onExitAnimationCancelled() {
+ isDialogShowing.value = false
+ }
+
+ override fun jankConfigurationBuilder(
+ cuj: Int
+ ): InteractionJankMonitor.Configuration.Builder? {
+ // TODO(b/252723237): Add support for jank monitoring when animating from a
+ // Composable.
+ return null
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 01e5d86549eb..1e74c3d68efc 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -39,19 +39,19 @@ interface ClockProvider {
fun getClocks(): List<ClockMetadata>
/** Initializes and returns the target clock design */
- fun createClock(id: ClockId): Clock
+ fun createClock(id: ClockId): ClockController
/** A static thumbnail for rendering in some examples */
fun getClockThumbnail(id: ClockId): Drawable?
}
/** Interface for controlling an active clock */
-interface Clock {
+interface ClockController {
/** A small version of the clock, appropriate for smaller viewports */
- val smallClock: View
+ val smallClock: ClockFaceController
/** A large version of the clock, appropriate when a bigger viewport is available */
- val largeClock: View
+ val largeClock: ClockFaceController
/** Events that clocks may need to respond to */
val events: ClockEvents
@@ -61,7 +61,7 @@ interface Clock {
/** Initializes various rendering parameters. If never called, provides reasonable defaults. */
fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
- events.onColorPaletteChanged(resources, true, true)
+ events.onColorPaletteChanged(resources)
animations.doze(dozeFraction)
animations.fold(foldFraction)
events.onTimeTick()
@@ -71,10 +71,19 @@ interface Clock {
fun dump(pw: PrintWriter) { }
}
+/** Interface for a specific clock face version rendered by the clock */
+interface ClockFaceController {
+ /** View that renders the clock face */
+ val view: View
+
+ /** Events specific to this clock face */
+ val events: ClockFaceEvents
+}
+
/** Events that should call when various rendering parameters change */
interface ClockEvents {
/** Call every time tick */
- fun onTimeTick()
+ fun onTimeTick() { }
/** Call whenever timezone changes */
fun onTimeZoneChanged(timeZone: TimeZone) { }
@@ -89,11 +98,7 @@ interface ClockEvents {
fun onFontSettingChanged() { }
/** Call whenever the color palette should update */
- fun onColorPaletteChanged(
- resources: Resources,
- smallClockIsDark: Boolean,
- largeClockIsDark: Boolean
- ) { }
+ fun onColorPaletteChanged(resources: Resources) { }
}
/** Methods which trigger various clock animations */
@@ -111,6 +116,12 @@ interface ClockAnimations {
fun charge() { }
}
+/** Events that have specific data about the related face */
+interface ClockFaceEvents {
+ /** Region Darkness specific to the clock face */
+ fun onRegionDarknessChanged(isDark: Boolean) { }
+}
+
/** Some data about a clock design */
data class ClockMetadata(
val clockId: ClockId,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java
index 506ccf3c2437..5f6f11c16da2 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java
@@ -67,7 +67,6 @@ public interface NavigationEdgeBackPlugin extends Plugin {
*
* @param triggerBack if back will be triggered in current state.
*/
- // TODO(b/247883311): Remove default impl once SwipeBackGestureHandler overrides this.
- default void setTriggerBack(boolean triggerBack) {}
+ void setTriggerBack(boolean triggerBack);
}
}
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
index 1ce106ed2156..2261ae8d7714 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
@@ -46,7 +46,7 @@
>
<com.android.systemui.statusbar.phone.MultiUserSwitch
- android:id="@+id/multi_user_switch"
+ android:id="@id/multi_user_switch"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
android:background="@drawable/qs_footer_action_circle"
@@ -61,7 +61,7 @@
</com.android.systemui.statusbar.phone.MultiUserSwitch>
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- android:id="@+id/settings_button_container"
+ android:id="@id/settings_button_container"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
android:background="@drawable/qs_footer_action_circle"
@@ -85,7 +85,7 @@
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
<com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@+id/pm_lite"
+ android:id="@id/pm_lite"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
android:background="@drawable/qs_footer_action_circle_color"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index 5486adbd2b8e..6ec65cebf840 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -24,7 +24,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
- androidprv:layout_maxHeight="@dimen/keyguard_security_height"
android:layout_gravity="center_horizontal|bottom"
android:gravity="bottom"
>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 8df8c49ee057..6120863f23ab 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -59,7 +59,7 @@
</LinearLayout>
- <ImageView
+ <com.android.systemui.common.ui.view.LaunchableImageView
android:id="@+id/start_button"
android:layout_height="@dimen/keyguard_affordance_fixed_height"
android:layout_width="@dimen/keyguard_affordance_fixed_width"
@@ -71,7 +71,7 @@
android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
android:visibility="gone" />
- <ImageView
+ <com.android.systemui.common.ui.view.LaunchableImageView
android:id="@+id/end_button"
android:layout_height="@dimen/keyguard_affordance_fixed_height"
android:layout_width="@dimen/keyguard_affordance_fixed_width"
diff --git a/packages/SystemUI/res/layout/screenshot.xml b/packages/SystemUI/res/layout/screenshot.xml
index c29e11bff624..c134c8e2d339 100644
--- a/packages/SystemUI/res/layout/screenshot.xml
+++ b/packages/SystemUI/res/layout/screenshot.xml
@@ -35,12 +35,6 @@
android:visibility="gone"
android:elevation="7dp"
android:src="@android:color/white"/>
- <com.android.systemui.screenshot.ScreenshotSelectorView
- android:id="@+id/screenshot_selector"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone"
- android:pointerIcon="crosshair"/>
<include layout="@layout/screenshot_static"
android:id="@+id/screenshot_static"/>
</com.android.systemui.screenshot.ScreenshotView>
diff --git a/packages/SystemUI/res/values-night/dimens.xml b/packages/SystemUI/res/values-night/dimens.xml
index d2d4198b7728..9fc86db48c80 100644
--- a/packages/SystemUI/res/values-night/dimens.xml
+++ b/packages/SystemUI/res/values-night/dimens.xml
@@ -20,4 +20,7 @@
<!-- Height of the background gradient behind the screenshot UI (taller in dark mode) -->
<dimen name="screenshot_bg_protection_height">375dp</dimen>
+ <!-- Accessibility floating menu -->
+ <dimen name="accessibility_floating_menu_stroke_width">1dp</dimen>
+ <dimen name="accessibility_floating_menu_stroke_inset">-2dp</dimen>
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
index bc7efe6a8682..ebe67d86ca23 100644
--- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
@@ -138,8 +138,8 @@
</string-array>
<string-array name="tile_states_cameratoggle">
<item msgid="6680671247180519913">"Indisponível"</item>
- <item msgid="4765607635752003190">"Desativada"</item>
- <item msgid="1697460731949649844">"Ativada"</item>
+ <item msgid="4765607635752003190">"Desligada"</item>
+ <item msgid="1697460731949649844">"Ligada"</item>
</string-array>
<string-array name="tile_states_mictoggle">
<item msgid="6895831614067195493">"Indisponível"</item>
diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
index 0a0102032c6a..bda7473ba15b 100644
--- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
@@ -138,8 +138,8 @@
</string-array>
<string-array name="tile_states_cameratoggle">
<item msgid="6680671247180519913">"Indisponível"</item>
- <item msgid="4765607635752003190">"Desligado"</item>
- <item msgid="1697460731949649844">"Ligado"</item>
+ <item msgid="4765607635752003190">"Desligada"</item>
+ <item msgid="1697460731949649844">"Ligada"</item>
</string-array>
<string-array name="tile_states_mictoggle">
<item msgid="6895831614067195493">"Indisponível"</item>
diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
index bc7efe6a8682..ebe67d86ca23 100644
--- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
@@ -138,8 +138,8 @@
</string-array>
<string-array name="tile_states_cameratoggle">
<item msgid="6680671247180519913">"Indisponível"</item>
- <item msgid="4765607635752003190">"Desativada"</item>
- <item msgid="1697460731949649844">"Ativada"</item>
+ <item msgid="4765607635752003190">"Desligada"</item>
+ <item msgid="1697460731949649844">"Ligada"</item>
</string-array>
<string-array name="tile_states_mictoggle">
<item msgid="6895831614067195493">"Indisponível"</item>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2c6e6068c4b1..fa3ed21e203e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1323,8 +1323,8 @@
<!-- Accessibility floating menu -->
<dimen name="accessibility_floating_menu_elevation">3dp</dimen>
- <dimen name="accessibility_floating_menu_stroke_width">1dp</dimen>
- <dimen name="accessibility_floating_menu_stroke_inset">-2dp</dimen>
+ <dimen name="accessibility_floating_menu_stroke_width">0dp</dimen>
+ <dimen name="accessibility_floating_menu_stroke_inset">0dp</dimen>
<dimen name="accessibility_floating_menu_margin">16dp</dimen>
<dimen name="accessibility_floating_menu_small_padding">6dp</dimen>
<dimen name="accessibility_floating_menu_small_width_height">36dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index f22e79722e78..7ca42f7d7015 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -188,5 +188,11 @@
<item type="id" name="face_scanning_anim"/>
<item type="id" name="qqs_tile_layout"/>
+
+ <!-- The buttons in the Quick Settings footer actions.-->
+ <item type="id" name="multi_user_switch"/>
+ <item type="id" name="pm_lite"/>
+ <item type="id" name="settings_button_container"/>
+
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index c2e74456c032..860a5da44088 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -58,6 +58,7 @@ class AnimatableClockView @JvmOverloads constructor(
private var lastOnTextChanged: CharSequence? = null
private var lastInvalidate: CharSequence? = null
private var lastTimeZoneChange: CharSequence? = null
+ private var lastAnimationCall: CharSequence? = null
private val time = Calendar.getInstance()
@@ -222,6 +223,7 @@ class AnimatableClockView @JvmOverloads constructor(
}
fun animateAppearOnLockscreen() {
+ lastAnimationCall = "${getTimestamp()} call=animateAppearOnLockscreen"
setTextStyle(
weight = dozingWeight,
textSize = -1f,
@@ -246,6 +248,7 @@ class AnimatableClockView @JvmOverloads constructor(
if (isAnimationEnabled && textAnimator == null) {
return
}
+ lastAnimationCall = "${getTimestamp()} call=animateFoldAppear"
setTextStyle(
weight = lockScreenWeightInternal,
textSize = -1f,
@@ -272,6 +275,7 @@ class AnimatableClockView @JvmOverloads constructor(
// Skip charge animation if dozing animation is already playing.
return
}
+ lastAnimationCall = "${getTimestamp()} call=animateCharge"
val startAnimPhase2 = Runnable {
setTextStyle(
weight = if (isDozing()) dozingWeight else lockScreenWeight,
@@ -295,6 +299,7 @@ class AnimatableClockView @JvmOverloads constructor(
}
fun animateDoze(isDozing: Boolean, animate: Boolean) {
+ lastAnimationCall = "${getTimestamp()} call=animateDoze"
setTextStyle(
weight = if (isDozing) dozingWeight else lockScreenWeight,
textSize = -1f,
@@ -408,6 +413,11 @@ class AnimatableClockView @JvmOverloads constructor(
pw.println(" lastTimeZoneChange=$lastTimeZoneChange")
pw.println(" currText=$text")
pw.println(" currTimeContextDesc=$contentDescription")
+ pw.println(" lastAnimationCall=$lastAnimationCall")
+ pw.println(" dozingWeightInternal=$dozingWeightInternal")
+ pw.println(" lockScreenWeightInternal=$lockScreenWeightInternal")
+ pw.println(" dozingColor=$dozingColor")
+ pw.println(" lockScreenColor=$lockScreenColor")
pw.println(" time=$time")
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 38a312448bab..f03fee4b0c2d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -22,7 +22,7 @@ import android.os.UserHandle
import android.provider.Settings
import android.util.Log
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.plugins.Clock
+import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProvider
@@ -33,7 +33,7 @@ import com.google.gson.Gson
import javax.inject.Inject
private val TAG = ClockRegistry::class.simpleName
-private val DEBUG = true
+private const val DEBUG = true
/** ClockRegistry aggregates providers and plugins */
open class ClockRegistry(
@@ -130,6 +130,10 @@ open class ClockRegistry(
}
availableClocks[id] = ClockInfo(clock, provider)
+ if (DEBUG) {
+ Log.i(TAG, "Added ${clock.clockId}")
+ }
+
if (currentId == id) {
if (DEBUG) {
Log.i(TAG, "Current clock ($currentId) was connected")
@@ -143,6 +147,9 @@ open class ClockRegistry(
val currentId = currentClockId
for (clock in provider.getClocks()) {
availableClocks.remove(clock.clockId)
+ if (DEBUG) {
+ Log.i(TAG, "Removed ${clock.clockId}")
+ }
if (currentId == clock.clockId) {
Log.w(TAG, "Current clock ($currentId) was disconnected")
@@ -161,7 +168,7 @@ open class ClockRegistry(
fun getClockThumbnail(clockId: ClockId): Drawable? =
availableClocks[clockId]?.provider?.getClockThumbnail(clockId)
- fun createExampleClock(clockId: ClockId): Clock? = createClock(clockId)
+ fun createExampleClock(clockId: ClockId): ClockController? = createClock(clockId)
fun registerClockChangeListener(listener: ClockChangeListener) =
clockChangeListeners.add(listener)
@@ -169,11 +176,14 @@ open class ClockRegistry(
fun unregisterClockChangeListener(listener: ClockChangeListener) =
clockChangeListeners.remove(listener)
- fun createCurrentClock(): Clock {
+ fun createCurrentClock(): ClockController {
val clockId = currentClockId
if (isEnabled && clockId.isNotEmpty()) {
val clock = createClock(clockId)
if (clock != null) {
+ if (DEBUG) {
+ Log.i(TAG, "Rendering clock $clockId")
+ }
return clock
} else {
Log.e(TAG, "Clock $clockId not found; using default")
@@ -183,7 +193,7 @@ open class ClockRegistry(
return createClock(DEFAULT_CLOCK_ID)!!
}
- private fun createClock(clockId: ClockId): Clock? =
+ private fun createClock(clockId: ClockId): ClockController? =
availableClocks[clockId]?.provider?.createClock(clockId)
private data class ClockInfo(
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
new file mode 100644
index 000000000000..b88795157a43
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2022 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.shared.clocks
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Color
+import android.icu.text.NumberFormat
+import android.util.TypedValue
+import android.view.LayoutInflater
+import android.widget.FrameLayout
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.plugins.ClockAnimations
+import com.android.systemui.plugins.ClockController
+import com.android.systemui.plugins.ClockEvents
+import com.android.systemui.plugins.ClockFaceController
+import com.android.systemui.plugins.ClockFaceEvents
+import com.android.systemui.shared.R
+import java.io.PrintWriter
+import java.util.Locale
+import java.util.TimeZone
+
+private val TAG = DefaultClockController::class.simpleName
+
+/**
+ * Controls the default clock visuals.
+ *
+ * This serves as an adapter between the clock interface and the AnimatableClockView used by the
+ * existing lockscreen clock.
+ */
+class DefaultClockController(
+ ctx: Context,
+ private val layoutInflater: LayoutInflater,
+ private val resources: Resources,
+) : ClockController {
+ override val smallClock: DefaultClockFaceController
+ override val largeClock: LargeClockFaceController
+ private val clocks: List<AnimatableClockView>
+
+ private val burmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"))
+ private val burmeseNumerals = burmeseNf.format(FORMAT_NUMBER.toLong())
+ private val burmeseLineSpacing =
+ resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese)
+ private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
+
+ override val events: DefaultClockEvents
+ override lateinit var animations: DefaultClockAnimations
+ private set
+
+ init {
+ val parent = FrameLayout(ctx)
+ smallClock =
+ DefaultClockFaceController(
+ layoutInflater.inflate(R.layout.clock_default_small, parent, false)
+ as AnimatableClockView
+ )
+ largeClock =
+ LargeClockFaceController(
+ layoutInflater.inflate(R.layout.clock_default_large, parent, false)
+ as AnimatableClockView
+ )
+ clocks = listOf(smallClock.view, largeClock.view)
+
+ events = DefaultClockEvents()
+ animations = DefaultClockAnimations(0f, 0f)
+ events.onLocaleChanged(Locale.getDefault())
+ }
+
+ override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
+ largeClock.recomputePadding()
+ animations = DefaultClockAnimations(dozeFraction, foldFraction)
+ events.onColorPaletteChanged(resources)
+ events.onTimeZoneChanged(TimeZone.getDefault())
+ events.onTimeTick()
+ }
+
+ open inner class DefaultClockFaceController(
+ override val view: AnimatableClockView,
+ ) : ClockFaceController {
+ // MAGENTA is a placeholder, and will be assigned correctly in initialize
+ private var currentColor = Color.MAGENTA
+ private var isRegionDark = false
+
+ init {
+ view.setColors(currentColor, currentColor)
+ }
+
+ override val events =
+ object : ClockFaceEvents {
+ override fun onRegionDarknessChanged(isRegionDark: Boolean) {
+ this@DefaultClockFaceController.isRegionDark = isRegionDark
+ updateColor()
+ }
+ }
+
+ fun updateColor() {
+ val color =
+ if (isRegionDark) {
+ resources.getColor(android.R.color.system_accent1_100)
+ } else {
+ resources.getColor(android.R.color.system_accent2_600)
+ }
+
+ if (currentColor == color) {
+ return
+ }
+
+ currentColor = color
+ view.setColors(DOZE_COLOR, color)
+ view.animateAppearOnLockscreen()
+ }
+ }
+
+ inner class LargeClockFaceController(
+ view: AnimatableClockView,
+ ) : DefaultClockFaceController(view) {
+ fun recomputePadding() {
+ val lp = view.getLayoutParams() as FrameLayout.LayoutParams
+ lp.topMargin = (-0.5f * view.bottom).toInt()
+ view.setLayoutParams(lp)
+ }
+ }
+
+ inner class DefaultClockEvents : ClockEvents {
+ override fun onTimeTick() = clocks.forEach { it.refreshTime() }
+
+ override fun onTimeFormatChanged(is24Hr: Boolean) =
+ clocks.forEach { it.refreshFormat(is24Hr) }
+
+ override fun onTimeZoneChanged(timeZone: TimeZone) =
+ clocks.forEach { it.onTimeZoneChanged(timeZone) }
+
+ override fun onFontSettingChanged() {
+ smallClock.view.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
+ resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
+ )
+ largeClock.view.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
+ resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
+ )
+ largeClock.recomputePadding()
+ }
+
+ override fun onColorPaletteChanged(resources: Resources) {
+ largeClock.updateColor()
+ smallClock.updateColor()
+ }
+
+ override fun onLocaleChanged(locale: Locale) {
+ val nf = NumberFormat.getInstance(locale)
+ if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) {
+ clocks.forEach { it.setLineSpacingScale(burmeseLineSpacing) }
+ } else {
+ clocks.forEach { it.setLineSpacingScale(defaultLineSpacing) }
+ }
+
+ clocks.forEach { it.refreshFormat() }
+ }
+ }
+
+ inner class DefaultClockAnimations(
+ dozeFraction: Float,
+ foldFraction: Float,
+ ) : ClockAnimations {
+ private var foldState = AnimationState(0f)
+ private var dozeState = AnimationState(0f)
+
+ init {
+ dozeState = AnimationState(dozeFraction)
+ foldState = AnimationState(foldFraction)
+
+ if (foldState.isActive) {
+ clocks.forEach { it.animateFoldAppear(false) }
+ } else {
+ clocks.forEach { it.animateDoze(dozeState.isActive, false) }
+ }
+ }
+
+ override fun enter() {
+ if (!dozeState.isActive) {
+ clocks.forEach { it.animateAppearOnLockscreen() }
+ }
+ }
+
+ override fun charge() = clocks.forEach { it.animateCharge { dozeState.isActive } }
+
+ override fun fold(fraction: Float) {
+ val (hasChanged, hasJumped) = foldState.update(fraction)
+ if (hasChanged) {
+ clocks.forEach { it.animateFoldAppear(!hasJumped) }
+ }
+ }
+
+ override fun doze(fraction: Float) {
+ val (hasChanged, hasJumped) = dozeState.update(fraction)
+ if (hasChanged) {
+ clocks.forEach { it.animateDoze(dozeState.isActive, !hasJumped) }
+ }
+ }
+ }
+
+ private class AnimationState(
+ var fraction: Float,
+ ) {
+ var isActive: Boolean = fraction < 0.5f
+ fun update(newFraction: Float): Pair<Boolean, Boolean> {
+ val wasActive = isActive
+ val hasJumped =
+ (fraction == 0f && newFraction == 1f) || (fraction == 1f && newFraction == 0f)
+ isActive = newFraction > fraction
+ fraction = newFraction
+ return Pair(wasActive != isActive, hasJumped)
+ }
+ }
+
+ override fun dump(pw: PrintWriter) {
+ pw.print("smallClock=")
+ smallClock.view.dump(pw)
+
+ pw.print("largeClock=")
+ largeClock.view.dump(pw)
+ }
+
+ companion object {
+ @VisibleForTesting const val DOZE_COLOR = Color.WHITE
+ private const val FORMAT_NUMBER = 1234567890
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 19ac2e479bcb..6627c5d8a1c5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -15,24 +15,14 @@ package com.android.systemui.shared.clocks
import android.content.Context
import android.content.res.Resources
-import android.graphics.Color
import android.graphics.drawable.Drawable
-import android.icu.text.NumberFormat
-import android.util.TypedValue
import android.view.LayoutInflater
-import android.widget.FrameLayout
-import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.plugins.Clock
-import com.android.systemui.plugins.ClockAnimations
-import com.android.systemui.plugins.ClockEvents
+import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProvider
import com.android.systemui.shared.R
-import java.io.PrintWriter
-import java.util.Locale
-import java.util.TimeZone
import javax.inject.Inject
private val TAG = DefaultClockProvider::class.simpleName
@@ -48,11 +38,12 @@ class DefaultClockProvider @Inject constructor(
override fun getClocks(): List<ClockMetadata> =
listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME))
- override fun createClock(id: ClockId): Clock {
+ override fun createClock(id: ClockId): ClockController {
if (id != DEFAULT_CLOCK_ID) {
throw IllegalArgumentException("$id is unsupported by $TAG")
}
- return DefaultClock(ctx, layoutInflater, resources)
+
+ return DefaultClockController(ctx, layoutInflater, resources)
}
override fun getClockThumbnail(id: ClockId): Drawable? {
@@ -64,190 +55,3 @@ class DefaultClockProvider @Inject constructor(
return resources.getDrawable(R.drawable.clock_default_thumbnail, null)
}
}
-
-/**
- * Controls the default clock visuals.
- *
- * This serves as an adapter between the clock interface and the
- * AnimatableClockView used by the existing lockscreen clock.
- */
-class DefaultClock(
- ctx: Context,
- private val layoutInflater: LayoutInflater,
- private val resources: Resources
-) : Clock {
- override val smallClock: AnimatableClockView
- override val largeClock: AnimatableClockView
- private val clocks get() = listOf(smallClock, largeClock)
-
- private val burmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"))
- private val burmeseNumerals = burmeseNf.format(FORMAT_NUMBER.toLong())
- private val burmeseLineSpacing =
- resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese)
- private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
-
- override val events: ClockEvents
- override lateinit var animations: ClockAnimations
- private set
-
- private var smallRegionDarkness = false
- private var largeRegionDarkness = false
-
- init {
- val parent = FrameLayout(ctx)
-
- smallClock = layoutInflater.inflate(
- R.layout.clock_default_small,
- parent,
- false
- ) as AnimatableClockView
-
- largeClock = layoutInflater.inflate(
- R.layout.clock_default_large,
- parent,
- false
- ) as AnimatableClockView
-
- events = DefaultClockEvents()
- animations = DefaultClockAnimations(0f, 0f)
-
- events.onLocaleChanged(Locale.getDefault())
-
- // DOZE_COLOR is a placeholder, and will be assigned correctly in initialize
- clocks.forEach { it.setColors(DOZE_COLOR, DOZE_COLOR) }
- }
-
- override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
- recomputePadding()
- animations = DefaultClockAnimations(dozeFraction, foldFraction)
- events.onColorPaletteChanged(resources, true, true)
- events.onTimeZoneChanged(TimeZone.getDefault())
- events.onTimeTick()
- }
-
- inner class DefaultClockEvents() : ClockEvents {
- override fun onTimeTick() = clocks.forEach { it.refreshTime() }
-
- override fun onTimeFormatChanged(is24Hr: Boolean) =
- clocks.forEach { it.refreshFormat(is24Hr) }
-
- override fun onTimeZoneChanged(timeZone: TimeZone) =
- clocks.forEach { it.onTimeZoneChanged(timeZone) }
-
- override fun onFontSettingChanged() {
- smallClock.setTextSize(
- TypedValue.COMPLEX_UNIT_PX,
- resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
- )
- largeClock.setTextSize(
- TypedValue.COMPLEX_UNIT_PX,
- resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
- )
- recomputePadding()
- }
-
- override fun onColorPaletteChanged(
- resources: Resources,
- smallClockIsDark: Boolean,
- largeClockIsDark: Boolean
- ) {
- if (smallRegionDarkness != smallClockIsDark) {
- smallRegionDarkness = smallClockIsDark
- updateClockColor(smallClock, smallClockIsDark)
- }
- if (largeRegionDarkness != largeClockIsDark) {
- largeRegionDarkness = largeClockIsDark
- updateClockColor(largeClock, largeClockIsDark)
- }
- }
-
- override fun onLocaleChanged(locale: Locale) {
- val nf = NumberFormat.getInstance(locale)
- if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) {
- clocks.forEach { it.setLineSpacingScale(burmeseLineSpacing) }
- } else {
- clocks.forEach { it.setLineSpacingScale(defaultLineSpacing) }
- }
-
- clocks.forEach { it.refreshFormat() }
- }
- }
-
- inner class DefaultClockAnimations(
- dozeFraction: Float,
- foldFraction: Float
- ) : ClockAnimations {
- private var foldState = AnimationState(0f)
- private var dozeState = AnimationState(0f)
-
- init {
- dozeState = AnimationState(dozeFraction)
- foldState = AnimationState(foldFraction)
-
- if (foldState.isActive) {
- clocks.forEach { it.animateFoldAppear(false) }
- } else {
- clocks.forEach { it.animateDoze(dozeState.isActive, false) }
- }
- }
-
- override fun enter() {
- if (!dozeState.isActive) {
- clocks.forEach { it.animateAppearOnLockscreen() }
- }
- }
-
- override fun charge() = clocks.forEach { it.animateCharge { dozeState.isActive } }
-
- override fun fold(fraction: Float) {
- val (hasChanged, hasJumped) = foldState.update(fraction)
- if (hasChanged) {
- clocks.forEach { it.animateFoldAppear(!hasJumped) }
- }
- }
-
- override fun doze(fraction: Float) {
- val (hasChanged, hasJumped) = dozeState.update(fraction)
- if (hasChanged) {
- clocks.forEach { it.animateDoze(dozeState.isActive, !hasJumped) }
- }
- }
- }
-
- private class AnimationState(
- var fraction: Float
- ) {
- var isActive: Boolean = fraction < 0.5f
- fun update(newFraction: Float): Pair<Boolean, Boolean> {
- val wasActive = isActive
- val hasJumped = (fraction == 0f && newFraction == 1f) ||
- (fraction == 1f && newFraction == 0f)
- isActive = newFraction > fraction
- fraction = newFraction
- return Pair(wasActive != isActive, hasJumped)
- }
- }
-
- private fun updateClockColor(clock: AnimatableClockView, isRegionDark: Boolean) {
- val color = if (isRegionDark) {
- resources.getColor(android.R.color.system_accent1_100)
- } else {
- resources.getColor(android.R.color.system_accent2_600)
- }
- clock.setColors(DOZE_COLOR, color)
- clock.animateAppearOnLockscreen()
- }
-
- private fun recomputePadding() {
- val lp = largeClock.getLayoutParams() as FrameLayout.LayoutParams
- lp.topMargin = (-0.5f * largeClock.bottom).toInt()
- largeClock.setLayoutParams(lp)
- }
-
- override fun dump(pw: PrintWriter) = clocks.forEach { it.dump(pw) }
-
- companion object {
- @VisibleForTesting const val DOZE_COLOR = Color.WHITE
- private const val FORMAT_NUMBER = 1234567890
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index ee30972296fb..80861722ac4f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -250,8 +250,9 @@ public class ActivityManagerWrapper {
public boolean startActivityFromRecents(int taskId, ActivityOptions options) {
try {
Bundle optsBundle = options == null ? null : options.toBundle();
- getService().startActivityFromRecents(taskId, optsBundle);
- return true;
+ return ActivityManager.isStartResultSuccessful(
+ getService().startActivityFromRecents(
+ taskId, optsBundle));
} catch (Exception e) {
return false;
}
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
index 74bd9c6c287d..bb3df8f0358a 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
@@ -18,6 +18,7 @@ package com.android.systemui.flags
import android.content.Context
import android.os.Handler
+import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsDebug.ALL_FLAGS
import com.android.systemui.util.settings.SettingsUtilModule
@@ -27,6 +28,7 @@ import dagger.Provides
import javax.inject.Named
@Module(includes = [
+ FeatureFlagsDebugStartableModule::class,
ServerFlagReaderModule::class,
SettingsUtilModule::class,
])
@@ -46,5 +48,15 @@ abstract class FlagsModule {
@Provides
@Named(ALL_FLAGS)
fun providesAllFlags(): Map<Int, Flag<*>> = Flags.collectFlags()
+
+ @JvmStatic
+ @Provides
+ fun providesRestarter(barService: IStatusBarService): Restarter {
+ return object: Restarter {
+ override fun restart() {
+ barService.restart()
+ }
+ }
+ }
}
}
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
index 38b5c9a9fa79..0f7e732fceb1 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
@@ -16,11 +16,29 @@
package com.android.systemui.flags
+import com.android.internal.statusbar.IStatusBarService
import dagger.Binds
import dagger.Module
+import dagger.Provides
-@Module(includes = [ServerFlagReaderModule::class])
+@Module(includes = [
+ FeatureFlagsReleaseStartableModule::class,
+ ServerFlagReaderModule::class
+])
abstract class FlagsModule {
@Binds
abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsRelease): FeatureFlags
+
+ @Module
+ companion object {
+ @JvmStatic
+ @Provides
+ fun providesRestarter(barService: IStatusBarService): Restarter {
+ return object: Restarter {
+ override fun restart() {
+ barService.restart()
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index 00f1c0108d0b..207f3440d38b 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -15,6 +15,12 @@
*/
package com.android.keyguard;
+import static androidx.constraintlayout.widget.ConstraintSet.BOTTOM;
+import static androidx.constraintlayout.widget.ConstraintSet.END;
+import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
+import static androidx.constraintlayout.widget.ConstraintSet.START;
+import static androidx.constraintlayout.widget.ConstraintSet.TOP;
+
import android.annotation.Nullable;
import android.app.admin.IKeyguardCallback;
import android.app.admin.IKeyguardClient;
@@ -30,7 +36,10 @@ import android.util.Log;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
-import android.view.ViewGroup;
+import android.view.View;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -49,7 +58,7 @@ public class AdminSecondaryLockScreenController {
private static final int REMOTE_CONTENT_READY_TIMEOUT_MILLIS = 500;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Context mContext;
- private final ViewGroup mParent;
+ private final ConstraintLayout mParent;
private AdminSecurityView mView;
private Handler mHandler;
private IKeyguardClient mClient;
@@ -156,6 +165,7 @@ public class AdminSecondaryLockScreenController {
mUpdateMonitor = updateMonitor;
mKeyguardCallback = callback;
mView = new AdminSecurityView(mContext, mSurfaceHolderCallback);
+ mView.setId(View.generateViewId());
}
/**
@@ -167,6 +177,15 @@ public class AdminSecondaryLockScreenController {
}
if (!mView.isAttachedToWindow()) {
mParent.addView(mView);
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(mParent);
+ constraintSet.connect(mView.getId(), TOP, PARENT_ID, TOP);
+ constraintSet.connect(mView.getId(), START, PARENT_ID, START);
+ constraintSet.connect(mView.getId(), END, PARENT_ID, END);
+ constraintSet.connect(mView.getId(), BOTTOM, PARENT_ID, BOTTOM);
+ constraintSet.constrainHeight(mView.getId(), ConstraintSet.MATCH_CONSTRAINT);
+ constraintSet.constrainWidth(mView.getId(), ConstraintSet.MATCH_CONSTRAINT);
+ constraintSet.applyTo(mParent);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index fd386618977c..9151238499dc 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -27,7 +27,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.plugins.Clock
+import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shared.regionsampling.RegionSamplingInstance
import com.android.systemui.statusbar.policy.BatteryController
@@ -44,18 +44,18 @@ import javax.inject.Inject
* [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
*/
open class ClockEventController @Inject constructor(
- private val statusBarStateController: StatusBarStateController,
- private val broadcastDispatcher: BroadcastDispatcher,
- private val batteryController: BatteryController,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val configurationController: ConfigurationController,
- @Main private val resources: Resources,
- private val context: Context,
- @Main private val mainExecutor: Executor,
- @Background private val bgExecutor: Executor,
- private val featureFlags: FeatureFlags
+ private val statusBarStateController: StatusBarStateController,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val batteryController: BatteryController,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val configurationController: ConfigurationController,
+ @Main private val resources: Resources,
+ private val context: Context,
+ @Main private val mainExecutor: Executor,
+ @Background private val bgExecutor: Executor,
+ private val featureFlags: FeatureFlags,
) {
- var clock: Clock? = null
+ var clock: ClockController? = null
set(value) {
field = value
if (value != null) {
@@ -74,42 +74,45 @@ open class ClockEventController @Inject constructor(
private val regionSamplingEnabled =
featureFlags.isEnabled(com.android.systemui.flags.Flags.REGION_SAMPLING)
- private val updateFun = object : RegionSamplingInstance.UpdateColorCallback {
- override fun updateColors() {
- if (regionSamplingEnabled) {
- smallClockIsDark = smallRegionSamplingInstance.currentRegionDarkness().isDark
- largeClockIsDark = largeRegionSamplingInstance.currentRegionDarkness().isDark
- } else {
- val isLightTheme = TypedValue()
- context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
- smallClockIsDark = isLightTheme.data == 0
- largeClockIsDark = isLightTheme.data == 0
- }
- clock?.events?.onColorPaletteChanged(resources, smallClockIsDark, largeClockIsDark)
+ private fun updateColors() {
+ if (regionSamplingEnabled && smallRegionSampler != null && largeRegionSampler != null) {
+ smallClockIsDark = smallRegionSampler!!.currentRegionDarkness().isDark
+ largeClockIsDark = largeRegionSampler!!.currentRegionDarkness().isDark
+ } else {
+ val isLightTheme = TypedValue()
+ context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
+ smallClockIsDark = isLightTheme.data == 0
+ largeClockIsDark = isLightTheme.data == 0
}
+
+ clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
+ clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
}
- fun updateRegionSamplers(currentClock: Clock?) {
- smallRegionSamplingInstance = createRegionSampler(
- currentClock?.smallClock,
+ private fun updateRegionSamplers(currentClock: ClockController?) {
+ smallRegionSampler?.stopRegionSampler()
+ largeRegionSampler?.stopRegionSampler()
+
+ smallRegionSampler = createRegionSampler(
+ currentClock?.smallClock?.view,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
- updateFun
+ ::updateColors
)
- largeRegionSamplingInstance = createRegionSampler(
- currentClock?.largeClock,
+ largeRegionSampler = createRegionSampler(
+ currentClock?.largeClock?.view,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
- updateFun
+ ::updateColors
)
- smallRegionSamplingInstance.startRegionSampler()
- largeRegionSamplingInstance.startRegionSampler()
+ smallRegionSampler!!.startRegionSampler()
+ largeRegionSampler!!.startRegionSampler()
- updateFun.updateColors()
+ updateColors()
}
protected open fun createRegionSampler(
@@ -117,25 +120,29 @@ open class ClockEventController @Inject constructor(
mainExecutor: Executor?,
bgExecutor: Executor?,
regionSamplingEnabled: Boolean,
- updateFun: RegionSamplingInstance.UpdateColorCallback
+ updateColors: () -> Unit
): RegionSamplingInstance {
return RegionSamplingInstance(
sampledView,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
- updateFun)
+ object : RegionSamplingInstance.UpdateColorCallback {
+ override fun updateColors() {
+ updateColors()
+ }
+ })
}
- lateinit var smallRegionSamplingInstance: RegionSamplingInstance
- lateinit var largeRegionSamplingInstance: RegionSamplingInstance
+ var smallRegionSampler: RegionSamplingInstance? = null
+ var largeRegionSampler: RegionSamplingInstance? = null
private var smallClockIsDark = true
private var largeClockIsDark = true
private val configListener = object : ConfigurationController.ConfigurationListener {
override fun onThemeChanged() {
- updateFun.updateColors()
+ clock?.events?.onColorPaletteChanged(resources)
}
override fun onDensityOrFontScaleChanged() {
@@ -204,8 +211,8 @@ open class ClockEventController @Inject constructor(
batteryController.addCallback(batteryCallback)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
statusBarStateController.addCallback(statusBarStateListener)
- smallRegionSamplingInstance.startRegionSampler()
- largeRegionSamplingInstance.startRegionSampler()
+ smallRegionSampler?.startRegionSampler()
+ largeRegionSampler?.startRegionSampler()
}
fun unregisterListeners() {
@@ -214,8 +221,8 @@ open class ClockEventController @Inject constructor(
batteryController.removeCallback(batteryCallback)
keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
statusBarStateController.removeCallback(statusBarStateListener)
- smallRegionSamplingInstance.stopRegionSampler()
- largeRegionSamplingInstance.stopRegionSampler()
+ smallRegionSampler?.stopRegionSampler()
+ largeRegionSampler?.stopRegionSampler()
}
/**
@@ -224,8 +231,8 @@ open class ClockEventController @Inject constructor(
fun dump(pw: PrintWriter) {
pw.println(this)
clock?.dump(pw)
- smallRegionSamplingInstance.dump(pw)
- largeRegionSamplingInstance.dump(pw)
+ smallRegionSampler?.dump(pw)
+ largeRegionSampler?.dump(pw)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index d7cd1d0dbc6f..d03ef984d42c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -17,7 +17,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.plugins.Clock;
+import com.android.systemui.plugins.ClockController;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -94,7 +94,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
onDensityOrFontScaleChanged();
}
- void setClock(Clock clock, int statusBarState) {
+ void setClock(ClockController clock, int statusBarState) {
// Disconnect from existing plugin.
mSmallClockFrame.removeAllViews();
mLargeClockFrame.removeAllViews();
@@ -105,11 +105,14 @@ public class KeyguardClockSwitch extends RelativeLayout {
}
// Attach small and big clock views to hierarchy.
- mSmallClockFrame.addView(clock.getSmallClock());
- mLargeClockFrame.addView(clock.getLargeClock());
+ Log.i(TAG, "Attached new clock views to switch");
+ mSmallClockFrame.addView(clock.getSmallClock().getView());
+ mLargeClockFrame.addView(clock.getLargeClock().getView());
}
private void updateClockViews(boolean useLargeClock, boolean animate) {
+ Log.i(TAG, "updateClockViews; useLargeClock=" + useLargeClock + "; animate=" + animate
+ + "; mChildrenAreLaidOut=" + mChildrenAreLaidOut);
if (mClockInAnim != null) mClockInAnim.cancel();
if (mClockOutAnim != null) mClockOutAnim.cancel();
if (mStatusAreaAnim != null) mStatusAreaAnim.cancel();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 2165099b474e..b450ec35db32 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -40,7 +40,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.plugins.Clock;
+import com.android.systemui.plugins.ClockController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
@@ -262,7 +262,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mCurrentClockSize = clockSize;
- Clock clock = getClock();
+ ClockController clock = getClock();
boolean appeared = mView.switchToClock(clockSize, animate);
if (clock != null && animate && appeared && clockSize == LARGE) {
clock.getAnimations().enter();
@@ -273,7 +273,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* Animates the clock view between folded and unfolded states
*/
public void animateFoldToAod(float foldFraction) {
- Clock clock = getClock();
+ ClockController clock = getClock();
if (clock != null) {
clock.getAnimations().fold(foldFraction);
}
@@ -286,7 +286,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
if (mSmartspaceController != null) {
mSmartspaceController.requestSmartspaceUpdate();
}
- Clock clock = getClock();
+ ClockController clock = getClock();
if (clock != null) {
clock.getEvents().onTimeTick();
}
@@ -319,17 +319,17 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* We can't directly getBottom() because clock changes positions in AOD for burn-in
*/
int getClockBottom(int statusBarHeaderHeight) {
- Clock clock = getClock();
+ ClockController clock = getClock();
if (clock == null) {
return 0;
}
if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
int frameHeight = mLargeClockFrame.getHeight();
- int clockHeight = clock.getLargeClock().getHeight();
+ int clockHeight = clock.getLargeClock().getView().getHeight();
return frameHeight / 2 + clockHeight / 2;
} else {
- int clockHeight = clock.getSmallClock().getHeight();
+ int clockHeight = clock.getSmallClock().getView().getHeight();
return clockHeight + statusBarHeaderHeight + mKeyguardClockTopMargin;
}
}
@@ -338,15 +338,15 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* Get the height of the currently visible clock on the keyguard.
*/
int getClockHeight() {
- Clock clock = getClock();
+ ClockController clock = getClock();
if (clock == null) {
return 0;
}
if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
- return clock.getLargeClock().getHeight();
+ return clock.getLargeClock().getView().getHeight();
} else {
- return clock.getSmallClock().getHeight();
+ return clock.getSmallClock().getView().getHeight();
}
}
@@ -361,12 +361,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mNotificationIconAreaController.setupAodIcons(nic);
}
- private void setClock(Clock clock) {
+ private void setClock(ClockController clock) {
mClockEventController.setClock(clock);
mView.setClock(clock, mStatusBarStateController.getState());
}
- private Clock getClock() {
+ private ClockController getClock() {
return mClockEventController.getClock();
}
@@ -398,7 +398,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("currentClockSizeLarge=" + (mCurrentClockSize == LARGE));
pw.println("mCanShowDoubleLineClock=" + mCanShowDoubleLineClock);
- Clock clock = getClock();
+ mView.dump(pw, args);
+ ClockController clock = getClock();
if (clock != null) {
clock.dump(pw);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 353c3698dce4..c34db1532d6c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -21,15 +21,23 @@ import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+import static androidx.constraintlayout.widget.ConstraintSet.BOTTOM;
+import static androidx.constraintlayout.widget.ConstraintSet.CHAIN_SPREAD;
+import static androidx.constraintlayout.widget.ConstraintSet.END;
+import static androidx.constraintlayout.widget.ConstraintSet.LEFT;
+import static androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT;
+import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
+import static androidx.constraintlayout.widget.ConstraintSet.RIGHT;
+import static androidx.constraintlayout.widget.ConstraintSet.START;
+import static androidx.constraintlayout.widget.ConstraintSet.TOP;
+import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT;
+
import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
import static java.lang.Integer.max;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
@@ -44,12 +52,12 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.UserManager;
import android.provider.Settings;
+import android.transition.TransitionManager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
import android.util.TypedValue;
import android.view.GestureDetector;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -59,8 +67,6 @@ import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowManager;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -68,8 +74,9 @@ import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -92,9 +99,9 @@ import com.android.systemui.util.settings.GlobalSettings;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.Consumer;
-public class KeyguardSecurityContainer extends FrameLayout {
+/** Determines how the bouncer is displayed to the user. */
+public class KeyguardSecurityContainer extends ConstraintLayout {
static final int USER_TYPE_PRIMARY = 1;
static final int USER_TYPE_WORK_PROFILE = 2;
static final int USER_TYPE_SECONDARY_USER = 3;
@@ -125,15 +132,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
// How much to scale the default slop by, to avoid accidental drags.
private static final float SLOP_SCALE = 4f;
- private static final long IME_DISAPPEAR_DURATION_MS = 125;
-
- // The duration of the animation to switch security sides.
- private static final long SECURITY_SHIFT_ANIMATION_DURATION_MS = 500;
-
- // How much of the switch sides animation should be dedicated to fading the security out. The
- // remainder will fade it back in again.
- private static final float SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION = 0.2f;
-
@VisibleForTesting
KeyguardSecurityViewFlipper mSecurityViewFlipper;
private GlobalSettings mGlobalSettings;
@@ -649,47 +647,8 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int maxHeight = 0;
- int maxWidth = 0;
- int childState = 0;
-
- for (int i = 0; i < getChildCount(); i++) {
- final View view = getChildAt(i);
- if (view.getVisibility() != GONE) {
- int updatedWidthMeasureSpec = mViewMode.getChildWidthMeasureSpec(widthMeasureSpec);
- final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-
- // When using EXACTLY spec, measure will use the layout width if > 0. Set before
- // measuring the child
- lp.width = MeasureSpec.getSize(updatedWidthMeasureSpec);
- measureChildWithMargins(view, updatedWidthMeasureSpec, 0,
- heightMeasureSpec, 0);
-
- maxWidth = Math.max(maxWidth,
- view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
- maxHeight = Math.max(maxHeight,
- view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
- childState = combineMeasuredStates(childState, view.getMeasuredState());
- }
- }
-
- maxWidth += getPaddingLeft() + getPaddingRight();
- maxHeight += getPaddingTop() + getPaddingBottom();
-
- // Check against our minimum height and width
- maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
- maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
-
- setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
- resolveSizeAndState(maxHeight, heightMeasureSpec,
- childState << MEASURED_HEIGHT_STATE_SHIFT));
- }
-
- @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
-
int width = right - left;
if (changed && mWidth != width) {
mWidth = width;
@@ -761,7 +720,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
* Enscapsulates the differences between bouncer modes for the container.
*/
interface ViewMode {
- default void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ default void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {};
@@ -787,11 +746,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
/** On notif tap, this animation will run */
default void startAppearAnimation(SecurityMode securityMode) {};
- /** Override to alter the width measure spec to perhaps limit the ViewFlipper size */
- default int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
- return parentWidthMeasureSpec;
- }
-
/** Called when we are setting a new ViewMode */
default void onDestroy() {};
}
@@ -801,13 +755,12 @@ public class KeyguardSecurityContainer extends FrameLayout {
* screen devices
*/
abstract static class SidedSecurityMode implements ViewMode {
- @Nullable private ValueAnimator mRunningSecurityShiftAnimator;
private KeyguardSecurityViewFlipper mViewFlipper;
- private ViewGroup mView;
+ private ConstraintLayout mView;
private GlobalSettings mGlobalSettings;
private int mDefaultSideSetting;
- public void init(ViewGroup v, KeyguardSecurityViewFlipper viewFlipper,
+ public void init(ConstraintLayout v, KeyguardSecurityViewFlipper viewFlipper,
GlobalSettings globalSettings, boolean leftAlignedByDefault) {
mView = v;
mViewFlipper = viewFlipper;
@@ -850,127 +803,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
protected abstract void updateSecurityViewLocation(boolean leftAlign, boolean animate);
- protected void translateSecurityViewLocation(boolean leftAlign, boolean animate) {
- translateSecurityViewLocation(leftAlign, animate, i -> {});
- }
-
- /**
- * Moves the inner security view to the correct location with animation. This is triggered
- * when the user double taps on the side of the screen that is not currently occupied by
- * the security view.
- */
- protected void translateSecurityViewLocation(boolean leftAlign, boolean animate,
- Consumer<Float> securityAlphaListener) {
- if (mRunningSecurityShiftAnimator != null) {
- mRunningSecurityShiftAnimator.cancel();
- mRunningSecurityShiftAnimator = null;
- }
-
- int targetTranslation = leftAlign
- ? 0 : mView.getMeasuredWidth() - mViewFlipper.getWidth();
-
- if (animate) {
- // This animation is a bit fun to implement. The bouncer needs to move, and fade
- // in/out at the same time. The issue is, the bouncer should only move a short
- // amount (120dp or so), but obviously needs to go from one side of the screen to
- // the other. This needs a pretty custom animation.
- //
- // This works as follows. It uses a ValueAnimation to simply drive the animation
- // progress. This animator is responsible for both the translation of the bouncer,
- // and the current fade. It will fade the bouncer out while also moving it along the
- // 120dp path. Once the bouncer is fully faded out though, it will "snap" the
- // bouncer closer to its destination, then fade it back in again. The effect is that
- // the bouncer will move from 0 -> X while fading out, then
- // (destination - X) -> destination while fading back in again.
- // TODO(b/208250221): Make this animation properly abortable.
- Interpolator positionInterpolator = AnimationUtils.loadInterpolator(
- mView.getContext(), android.R.interpolator.fast_out_extra_slow_in);
- Interpolator fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN;
- Interpolator fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
-
- mRunningSecurityShiftAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
- mRunningSecurityShiftAnimator.setDuration(SECURITY_SHIFT_ANIMATION_DURATION_MS);
- mRunningSecurityShiftAnimator.setInterpolator(Interpolators.LINEAR);
-
- int initialTranslation = (int) mViewFlipper.getTranslationX();
- int totalTranslation = (int) mView.getResources().getDimension(
- R.dimen.security_shift_animation_translation);
-
- final boolean shouldRestoreLayerType = mViewFlipper.hasOverlappingRendering()
- && mViewFlipper.getLayerType() != View.LAYER_TYPE_HARDWARE;
- if (shouldRestoreLayerType) {
- mViewFlipper.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */null);
- }
-
- float initialAlpha = mViewFlipper.getAlpha();
-
- mRunningSecurityShiftAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mRunningSecurityShiftAnimator = null;
- }
- });
- mRunningSecurityShiftAnimator.addUpdateListener(animation -> {
- float switchPoint = SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION;
- boolean isFadingOut = animation.getAnimatedFraction() < switchPoint;
-
- int currentTranslation = (int) (positionInterpolator.getInterpolation(
- animation.getAnimatedFraction()) * totalTranslation);
- int translationRemaining = totalTranslation - currentTranslation;
-
- // Flip the sign if we're going from right to left.
- if (leftAlign) {
- currentTranslation = -currentTranslation;
- translationRemaining = -translationRemaining;
- }
-
- float opacity;
- if (isFadingOut) {
- // The bouncer fades out over the first X%.
- float fadeOutFraction = MathUtils.constrainedMap(
- /* rangeMin= */1.0f,
- /* rangeMax= */0.0f,
- /* valueMin= */0.0f,
- /* valueMax= */switchPoint,
- animation.getAnimatedFraction());
- opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction);
-
- // When fading out, the alpha needs to start from the initial opacity of the
- // view flipper, otherwise we get a weird bit of jank as it ramps back to
- // 100%.
- mViewFlipper.setAlpha(opacity * initialAlpha);
-
- // Animate away from the source.
- mViewFlipper.setTranslationX(initialTranslation + currentTranslation);
- } else {
- // And in again over the remaining (100-X)%.
- float fadeInFraction = MathUtils.constrainedMap(
- /* rangeMin= */0.0f,
- /* rangeMax= */1.0f,
- /* valueMin= */switchPoint,
- /* valueMax= */1.0f,
- animation.getAnimatedFraction());
-
- opacity = fadeInInterpolator.getInterpolation(fadeInFraction);
- mViewFlipper.setAlpha(opacity);
-
- // Fading back in, animate towards the destination.
- mViewFlipper.setTranslationX(targetTranslation - translationRemaining);
- }
- securityAlphaListener.accept(opacity);
-
- if (animation.getAnimatedFraction() == 1.0f && shouldRestoreLayerType) {
- mViewFlipper.setLayerType(View.LAYER_TYPE_NONE, /* paint= */null);
- }
- });
-
- mRunningSecurityShiftAnimator.start();
- } else {
- mViewFlipper.setTranslationX(targetTranslation);
- }
- }
-
-
boolean isLeftAligned() {
return mGlobalSettings.getInt(Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
mDefaultSideSetting)
@@ -989,11 +821,11 @@ public class KeyguardSecurityContainer extends FrameLayout {
* Default bouncer is centered within the space
*/
static class DefaultViewMode implements ViewMode {
- private ViewGroup mView;
+ private ConstraintLayout mView;
private KeyguardSecurityViewFlipper mViewFlipper;
@Override
- public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ public void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {
@@ -1005,11 +837,14 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
private void updateSecurityViewGroup() {
- FrameLayout.LayoutParams lp =
- (FrameLayout.LayoutParams) mViewFlipper.getLayoutParams();
- lp.gravity = Gravity.CENTER_HORIZONTAL;
- mViewFlipper.setLayoutParams(lp);
- mViewFlipper.setTranslationX(0);
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.connect(mViewFlipper.getId(), START, PARENT_ID, START);
+ constraintSet.connect(mViewFlipper.getId(), END, PARENT_ID, END);
+ constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
+ constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
+ constraintSet.constrainHeight(mViewFlipper.getId(), MATCH_CONSTRAINT);
+ constraintSet.constrainWidth(mViewFlipper.getId(), MATCH_CONSTRAINT);
+ constraintSet.applyTo(mView);
}
}
@@ -1018,7 +853,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
* a user switcher, in both portrait and landscape modes.
*/
static class UserSwitcherViewMode extends SidedSecurityMode {
- private ViewGroup mView;
+ private ConstraintLayout mView;
private ViewGroup mUserSwitcherViewGroup;
private KeyguardSecurityViewFlipper mViewFlipper;
private TextView mUserSwitcher;
@@ -1029,11 +864,8 @@ public class KeyguardSecurityContainer extends FrameLayout {
private UserSwitcherController.UserSwitchCallback mUserSwitchCallback =
this::setupUserSwitcher;
- private float mAnimationLastAlpha = 1f;
- private boolean mAnimationWaitsToShift = true;
-
@Override
- public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ public void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {
@@ -1240,89 +1072,56 @@ public class KeyguardSecurityContainer extends FrameLayout {
});
}
- /**
- * Each view will get half the width. Yes, it would be easier to use something other than
- * FrameLayout but it was too disruptive to downstream projects to change.
- */
- @Override
- public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
- return MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(parentWidthMeasureSpec) / 2,
- MeasureSpec.EXACTLY);
- }
-
@Override
public void updateSecurityViewLocation() {
updateSecurityViewLocation(isLeftAligned(), /* animate= */false);
}
public void updateSecurityViewLocation(boolean leftAlign, boolean animate) {
- setYTranslation();
- setGravity();
- setXTranslation(leftAlign, animate);
- }
-
- private void setXTranslation(boolean leftAlign, boolean animate) {
- if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- mUserSwitcherViewGroup.setTranslationX(0);
- mViewFlipper.setTranslationX(0);
- } else {
- int switcherTargetTranslation = leftAlign
- ? mView.getMeasuredWidth() - mViewFlipper.getWidth() : 0;
- if (animate) {
- mAnimationWaitsToShift = true;
- mAnimationLastAlpha = 1f;
- translateSecurityViewLocation(leftAlign, animate, securityAlpha -> {
- // During the animation security view fades out - alpha goes from 1 to
- // (almost) 0 - and then fades in - alpha grows back to 1.
- // If new alpha is bigger than previous one it means we're at inflection
- // point and alpha is zero or almost zero. That's when we want to do
- // translation of user switcher, so that it's not visible to the user.
- boolean fullyFadeOut = securityAlpha == 0.0f
- || securityAlpha > mAnimationLastAlpha;
- if (fullyFadeOut && mAnimationWaitsToShift) {
- mUserSwitcherViewGroup.setTranslationX(switcherTargetTranslation);
- mAnimationWaitsToShift = false;
- }
- mUserSwitcherViewGroup.setAlpha(securityAlpha);
- mAnimationLastAlpha = securityAlpha;
- });
- } else {
- translateSecurityViewLocation(leftAlign, animate);
- mUserSwitcherViewGroup.setTranslationX(switcherTargetTranslation);
- }
- }
-
- }
-
- private void setGravity() {
- if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- updateViewGravity(mUserSwitcherViewGroup, Gravity.CENTER_HORIZONTAL);
- updateViewGravity(mViewFlipper, Gravity.CENTER_HORIZONTAL);
- } else {
- // horizontal gravity is the same because we translate these views anyway
- updateViewGravity(mViewFlipper, Gravity.LEFT | Gravity.BOTTOM);
- updateViewGravity(mUserSwitcherViewGroup, Gravity.LEFT | Gravity.CENTER_VERTICAL);
+ if (animate) {
+ TransitionManager.beginDelayedTransition(mView,
+ new KeyguardSecurityViewTransition());
}
- }
-
- private void setYTranslation() {
int yTrans = mResources.getDimensionPixelSize(R.dimen.bouncer_user_switcher_y_trans);
if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- mUserSwitcherViewGroup.setTranslationY(yTrans);
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.connect(mUserSwitcherViewGroup.getId(), TOP, PARENT_ID, TOP, yTrans);
+ constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
+ constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
+ constraintSet.centerHorizontally(mViewFlipper.getId(), PARENT_ID);
+ constraintSet.centerHorizontally(mUserSwitcherViewGroup.getId(), PARENT_ID);
+ constraintSet.setVerticalChainStyle(mViewFlipper.getId(), CHAIN_SPREAD);
+ constraintSet.setVerticalChainStyle(mUserSwitcherViewGroup.getId(), CHAIN_SPREAD);
+ constraintSet.constrainHeight(mUserSwitcherViewGroup.getId(), WRAP_CONTENT);
+ constraintSet.constrainWidth(mUserSwitcherViewGroup.getId(), WRAP_CONTENT);
+ constraintSet.constrainHeight(mViewFlipper.getId(), MATCH_CONSTRAINT);
+ constraintSet.applyTo(mView);
} else {
- // Attempt to reposition a bit higher to make up for this frame being a bit lower
- // on the device
- mUserSwitcherViewGroup.setTranslationY(-yTrans);
- mViewFlipper.setTranslationY(0);
+ int leftElement = leftAlign ? mViewFlipper.getId() : mUserSwitcherViewGroup.getId();
+ int rightElement =
+ leftAlign ? mUserSwitcherViewGroup.getId() : mViewFlipper.getId();
+
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.connect(leftElement, LEFT, PARENT_ID, LEFT);
+ constraintSet.connect(leftElement, RIGHT, rightElement, LEFT);
+ constraintSet.connect(rightElement, LEFT, leftElement, RIGHT);
+ constraintSet.connect(rightElement, RIGHT, PARENT_ID, RIGHT);
+ constraintSet.connect(mUserSwitcherViewGroup.getId(), TOP, PARENT_ID, TOP);
+ constraintSet.connect(mUserSwitcherViewGroup.getId(), BOTTOM, PARENT_ID, BOTTOM,
+ yTrans);
+ constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
+ constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
+ constraintSet.setHorizontalChainStyle(mUserSwitcherViewGroup.getId(), CHAIN_SPREAD);
+ constraintSet.setHorizontalChainStyle(mViewFlipper.getId(), CHAIN_SPREAD);
+ constraintSet.constrainHeight(mUserSwitcherViewGroup.getId(),
+ MATCH_CONSTRAINT);
+ constraintSet.constrainWidth(mUserSwitcherViewGroup.getId(),
+ MATCH_CONSTRAINT);
+ constraintSet.constrainWidth(mViewFlipper.getId(), MATCH_CONSTRAINT);
+ constraintSet.constrainHeight(mViewFlipper.getId(), MATCH_CONSTRAINT);
+ constraintSet.applyTo(mView);
}
}
-
- private void updateViewGravity(View v, int gravity) {
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
- lp.gravity = gravity;
- v.setLayoutParams(lp);
- }
}
/**
@@ -1330,11 +1129,11 @@ public class KeyguardSecurityContainer extends FrameLayout {
* between alternate sides of the display.
*/
static class OneHandedViewMode extends SidedSecurityMode {
- private ViewGroup mView;
+ private ConstraintLayout mView;
private KeyguardSecurityViewFlipper mViewFlipper;
@Override
- public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ public void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {
@@ -1342,28 +1141,10 @@ public class KeyguardSecurityContainer extends FrameLayout {
mView = v;
mViewFlipper = viewFlipper;
- updateSecurityViewGravity();
updateSecurityViewLocation(isLeftAligned(), /* animate= */false);
}
/**
- * One-handed mode contains the child to half of the available space.
- */
- @Override
- public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
- return MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(parentWidthMeasureSpec) / 2,
- MeasureSpec.EXACTLY);
- }
-
- private void updateSecurityViewGravity() {
- FrameLayout.LayoutParams lp =
- (FrameLayout.LayoutParams) mViewFlipper.getLayoutParams();
- lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
- mViewFlipper.setLayoutParams(lp);
- }
-
- /**
* Moves the bouncer to align with a tap (most likely in the shade), so the bouncer
* appears on the same side as a touch.
*/
@@ -1380,7 +1161,20 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
protected void updateSecurityViewLocation(boolean leftAlign, boolean animate) {
- translateSecurityViewLocation(leftAlign, animate);
+ if (animate) {
+ TransitionManager.beginDelayedTransition(mView,
+ new KeyguardSecurityViewTransition());
+ }
+ ConstraintSet constraintSet = new ConstraintSet();
+ if (leftAlign) {
+ constraintSet.connect(mViewFlipper.getId(), LEFT, PARENT_ID, LEFT);
+ } else {
+ constraintSet.connect(mViewFlipper.getId(), RIGHT, PARENT_ID, RIGHT);
+ }
+ constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
+ constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
+ constraintSet.constrainPercentWidth(mViewFlipper.getId(), 0.5f);
+ constraintSet.applyTo(mView);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
new file mode 100644
index 000000000000..9eb2c118abac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2022 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.keyguard
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.graphics.Rect
+import android.transition.Transition
+import android.transition.TransitionValues
+import android.util.MathUtils
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.AnimationUtils
+import com.android.internal.R.interpolator.fast_out_extra_slow_in
+import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
+
+/** Animates constraint layout changes for the security view. */
+class KeyguardSecurityViewTransition : Transition() {
+
+ companion object {
+ const val PROP_BOUNDS = "securityViewLocation:bounds"
+
+ // The duration of the animation to switch security sides.
+ const val SECURITY_SHIFT_ANIMATION_DURATION_MS = 500L
+
+ // How much of the switch sides animation should be dedicated to fading the security out.
+ // The remainder will fade it back in again.
+ const val SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION = 0.2f
+ }
+
+ private fun captureValues(values: TransitionValues) {
+ val boundsRect = Rect()
+ boundsRect.left = values.view.left
+ boundsRect.top = values.view.top
+ boundsRect.right = values.view.right
+ boundsRect.bottom = values.view.bottom
+ values.values[PROP_BOUNDS] = boundsRect
+ }
+
+ override fun getTransitionProperties(): Array<String>? {
+ return arrayOf(PROP_BOUNDS)
+ }
+
+ override fun captureEndValues(transitionValues: TransitionValues?) {
+ transitionValues?.let { captureValues(it) }
+ }
+
+ override fun captureStartValues(transitionValues: TransitionValues?) {
+ transitionValues?.let { captureValues(it) }
+ }
+
+ override fun createAnimator(
+ sceneRoot: ViewGroup?,
+ startValues: TransitionValues?,
+ endValues: TransitionValues?
+ ): Animator? {
+ if (sceneRoot == null || startValues == null || endValues == null) {
+ return null
+ }
+
+ // This animation is a bit fun to implement. The bouncer needs to move, and fade
+ // in/out at the same time. The issue is, the bouncer should only move a short
+ // amount (120dp or so), but obviously needs to go from one side of the screen to
+ // the other. This needs a pretty custom animation.
+ //
+ // This works as follows. It uses a ValueAnimation to simply drive the animation
+ // progress. This animator is responsible for both the translation of the bouncer,
+ // and the current fade. It will fade the bouncer out while also moving it along the
+ // 120dp path. Once the bouncer is fully faded out though, it will "snap" the
+ // bouncer closer to its destination, then fade it back in again. The effect is that
+ // the bouncer will move from 0 -> X while fading out, then
+ // (destination - X) -> destination while fading back in again.
+ // TODO(b/208250221): Make this animation properly abortable.
+ val positionInterpolator =
+ AnimationUtils.loadInterpolator(sceneRoot.context, fast_out_extra_slow_in)
+ val fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN
+ val fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN
+ var runningSecurityShiftAnimator = ValueAnimator.ofFloat(0.0f, 1.0f)
+ runningSecurityShiftAnimator.duration = SECURITY_SHIFT_ANIMATION_DURATION_MS
+ runningSecurityShiftAnimator.interpolator = Interpolators.LINEAR
+ val startRect = startValues.values[PROP_BOUNDS] as Rect
+ val endRect = endValues.values[PROP_BOUNDS] as Rect
+ val v = startValues.view
+ val totalTranslation: Int =
+ sceneRoot.resources.getDimension(R.dimen.security_shift_animation_translation).toInt()
+ val shouldRestoreLayerType =
+ (v.hasOverlappingRendering() && v.layerType != View.LAYER_TYPE_HARDWARE)
+ if (shouldRestoreLayerType) {
+ v.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */ null)
+ }
+ val initialAlpha: Float = v.alpha
+ runningSecurityShiftAnimator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ runningSecurityShiftAnimator = null
+ }
+ }
+ )
+
+ var finishedFadingOutNonSecurityView = false
+
+ runningSecurityShiftAnimator.addUpdateListener { animation: ValueAnimator ->
+ val switchPoint = SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION
+ val isFadingOut = animation.animatedFraction < switchPoint
+ val opacity: Float
+ var currentTranslation =
+ (positionInterpolator.getInterpolation(animation.animatedFraction) *
+ totalTranslation)
+ .toInt()
+ var translationRemaining = totalTranslation - currentTranslation
+ val leftAlign = endRect.left < startRect.left
+ if (leftAlign) {
+ currentTranslation = -currentTranslation
+ translationRemaining = -translationRemaining
+ }
+
+ if (isFadingOut) {
+ // The bouncer fades out over the first X%.
+ val fadeOutFraction =
+ MathUtils.constrainedMap(
+ /* rangeMin= */ 1.0f,
+ /* rangeMax= */ 0.0f,
+ /* valueMin= */ 0.0f,
+ /* valueMax= */ switchPoint,
+ animation.animatedFraction
+ )
+ opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction)
+
+ // When fading out, the alpha needs to start from the initial opacity of the
+ // view flipper, otherwise we get a weird bit of jank as it ramps back to
+ // 100%.
+ v.alpha = opacity * initialAlpha
+ if (v is KeyguardSecurityViewFlipper) {
+ v.setLeftTopRightBottom(
+ startRect.left + currentTranslation,
+ startRect.top,
+ startRect.right + currentTranslation,
+ startRect.bottom
+ )
+ }
+ } else {
+ // And in again over the remaining (100-X)%.
+ val fadeInFraction =
+ MathUtils.constrainedMap(
+ /* rangeMin= */ 0.0f,
+ /* rangeMax= */ 1.0f,
+ /* valueMin= */ switchPoint,
+ /* valueMax= */ 1.0f,
+ animation.animatedFraction
+ )
+ opacity = fadeInInterpolator.getInterpolation(fadeInFraction)
+ v.alpha = opacity
+
+ // Fading back in, animate towards the destination.
+ if (v is KeyguardSecurityViewFlipper) {
+ v.setLeftTopRightBottom(
+ endRect.left - translationRemaining,
+ endRect.top,
+ endRect.right - translationRemaining,
+ endRect.bottom
+ )
+ }
+ }
+ if (animation.animatedFraction == 1.0f && shouldRestoreLayerType) {
+ v.setLayerType(View.LAYER_TYPE_NONE, /* paint= */ null)
+ }
+
+ // For views that are not the security view flipper, we do not want to apply
+ // an x translation animation. Instead, we want to fade out, move to final position and
+ // then fade in.
+ if (v !is KeyguardSecurityViewFlipper) {
+ // Opacity goes close to 0 but does not fully get to 0.
+ if (opacity - 0.001f < 0f) {
+ v.setLeftTopRightBottom(
+ endRect.left,
+ endRect.top,
+ endRect.right,
+ endRect.bottom
+ )
+ finishedFadingOutNonSecurityView = true
+ } else if (!finishedFadingOutNonSecurityView) {
+ v.setLeftTopRightBottom(
+ startRect.left,
+ startRect.top,
+ startRect.right,
+ startRect.bottom
+ )
+ }
+ }
+ }
+ runningSecurityShiftAnimator.start()
+ return runningSecurityShiftAnimator
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 761aa7e9b09c..80120a9de420 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2333,7 +2333,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
*/
public void requestFaceAuth(boolean userInitiatedRequest,
@FaceAuthApiRequestReason String reason) {
- mLogger.logFaceAuthRequested(userInitiatedRequest);
+ mLogger.logFaceAuthRequested(userInitiatedRequest, reason);
updateFaceListeningState(BIOMETRIC_ACTION_START, apiRequestReasonToUiEvent(reason));
}
@@ -3164,14 +3164,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
* Whether the keyguard is showing and not occluded.
*/
public boolean isKeyguardVisible() {
- return isKeyguardShowing() && !mKeyguardOccluded;
- }
-
- /**
- * Whether the keyguard is showing. It may still be occluded and not visible.
- */
- public boolean isKeyguardShowing() {
- return mKeyguardShowing;
+ return mKeyguardShowing && !mKeyguardOccluded;
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index 3ea882667d9f..90f0446ee34d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -94,11 +94,6 @@ public interface KeyguardViewController {
void setOccluded(boolean occluded, boolean animate);
/**
- * @return Whether the keyguard is showing
- */
- boolean isShowing();
-
- /**
* Dismisses the keyguard by going to the next screen or making it gone.
*/
void dismissAndCollapse();
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 54cec7132bfa..2eee95738b7b 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -108,10 +108,11 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
}, { "Face help received, msgId: $int1 msg: $str1" })
}
- fun logFaceAuthRequested(userInitiatedRequest: Boolean) {
- logBuffer.log(TAG, DEBUG,
- { bool1 = userInitiatedRequest },
- { "requestFaceAuth() userInitiated=$bool1" })
+ fun logFaceAuthRequested(userInitiatedRequest: Boolean, reason: String) {
+ logBuffer.log(TAG, DEBUG, {
+ bool1 = userInitiatedRequest
+ str1 = reason
+ }, { "requestFaceAuth() userInitiated=$bool1 reason=$str1" })
}
fun logFaceAuthSuccess(userId: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
index 109be40ce10f..37829f25d179 100644
--- a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
+++ b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
@@ -3,6 +3,7 @@ package com.android.systemui
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
+import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -55,7 +56,11 @@ class ChooserSelector @Inject constructor(
} else {
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
}
- packageManager.setComponentEnabledSetting(chooserComponent, newState, /* flags = */ 0)
+ try {
+ packageManager.setComponentEnabledSetting(chooserComponent, newState, /* flags = */ 0)
+ } catch (e: IllegalArgumentException) {
+ Log.w("ChooserSelector", "Unable to set IntentResolver enabled=" + enabled, e)
+ }
}
suspend inline fun awaitCancellation(): Nothing = suspendCancellableCoroutine { }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index 403941f8e639..ea334b27fa09 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -17,10 +17,17 @@
package com.android.systemui.accessibility.floatingmenu;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+
+import static com.android.systemui.flags.Flags.A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS;
import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.view.Display;
+import android.view.WindowManager;
import androidx.annotation.MainThread;
@@ -31,6 +38,7 @@ import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver.AccessibilityButtonMode;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.flags.FeatureFlags;
import javax.inject.Inject;
@@ -46,6 +54,9 @@ public class AccessibilityFloatingMenuController implements
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private Context mContext;
+ private final WindowManager mWindowManager;
+ private final DisplayManager mDisplayManager;
+ private final FeatureFlags mFeatureFlags;
@VisibleForTesting
IAccessibilityFloatingMenu mFloatingMenu;
private int mBtnMode;
@@ -83,13 +94,19 @@ public class AccessibilityFloatingMenuController implements
@Inject
public AccessibilityFloatingMenuController(Context context,
+ WindowManager windowManager,
+ DisplayManager displayManager,
AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
AccessibilityButtonModeObserver accessibilityButtonModeObserver,
- KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ FeatureFlags featureFlags) {
mContext = context;
+ mWindowManager = windowManager;
+ mDisplayManager = displayManager;
mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mFeatureFlags = featureFlags;
mIsKeyguardVisible = false;
}
@@ -159,7 +176,14 @@ public class AccessibilityFloatingMenuController implements
private void showFloatingMenu() {
if (mFloatingMenu == null) {
- mFloatingMenu = new AccessibilityFloatingMenu(mContext);
+ if (mFeatureFlags.isEnabled(A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS)) {
+ final Display defaultDisplay = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+ mFloatingMenu = new MenuViewLayerController(
+ mContext.createWindowContext(defaultDisplay,
+ TYPE_NAVIGATION_BAR_PANEL, /* options= */ null), mWindowManager);
+ } else {
+ mFloatingMenu = new AccessibilityFloatingMenu(mContext);
+ }
}
mFloatingMenu.show();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
new file mode 100644
index 000000000000..698d60a5b13e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 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.accessibility.floatingmenu;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE;
+import static android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+
+import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
+import static com.android.systemui.accessibility.floatingmenu.MenuViewAppearance.MenuSizeType.SMALL;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.internal.accessibility.dialog.AccessibilityTarget;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * Stores and observe the settings contents for the menu view.
+ */
+class MenuInfoRepository {
+ private final Context mContext;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final OnSettingsContentsChanged mSettingsContentsCallback;
+
+ private final ContentObserver mMenuTargetFeaturesContentObserver =
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mSettingsContentsCallback.onTargetFeaturesChanged(
+ getTargets(mContext, ACCESSIBILITY_BUTTON));
+ }
+ };
+
+ @VisibleForTesting
+ final ContentObserver mMenuSizeContentObserver =
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mSettingsContentsCallback.onSizeTypeChanged(
+ getMenuSizeTypeFromSettings(mContext));
+ }
+ };
+
+ MenuInfoRepository(Context context, OnSettingsContentsChanged settingsContentsChanged) {
+ mContext = context;
+ mSettingsContentsCallback = settingsContentsChanged;
+ }
+
+ void loadMenuTargetFeatures(OnInfoReady<List<AccessibilityTarget>> callback) {
+ callback.onReady(getTargets(mContext, ACCESSIBILITY_BUTTON));
+ }
+
+ void loadMenuSizeType(OnInfoReady<Integer> callback) {
+ callback.onReady(getMenuSizeTypeFromSettings(mContext));
+ }
+
+ void registerContentObservers() {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
+ /* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
+ UserHandle.USER_CURRENT);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
+ /* notifyForDescendants */ false,
+ mMenuTargetFeaturesContentObserver, UserHandle.USER_CURRENT);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
+ /* notifyForDescendants */ false, mMenuSizeContentObserver,
+ UserHandle.USER_CURRENT);
+ }
+
+ void unregisterContentObservers() {
+ mContext.getContentResolver().unregisterContentObserver(mMenuTargetFeaturesContentObserver);
+ mContext.getContentResolver().unregisterContentObserver(mMenuSizeContentObserver);
+ }
+
+ interface OnSettingsContentsChanged {
+ void onTargetFeaturesChanged(List<AccessibilityTarget> newTargetFeatures);
+
+ void onSizeTypeChanged(int newSizeType);
+ }
+
+ interface OnInfoReady<T> {
+ void onReady(T info);
+ }
+
+ private static int getMenuSizeTypeFromSettings(Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ ACCESSIBILITY_FLOATING_MENU_SIZE, SMALL, UserHandle.USER_CURRENT);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
new file mode 100644
index 000000000000..576f23ee780e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2022 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.accessibility.floatingmenu;
+
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.drawable.GradientDrawable;
+import android.widget.FrameLayout;
+
+import androidx.lifecycle.Observer;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.accessibility.dialog.AccessibilityTarget;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The container view displays the accessibility features.
+ */
+@SuppressLint("ViewConstructor")
+class MenuView extends FrameLayout {
+ private static final int INDEX_MENU_ITEM = 0;
+ private final List<AccessibilityTarget> mTargetFeatures = new ArrayList<>();
+ private final AccessibilityTargetAdapter mAdapter;
+ private final MenuViewModel mMenuViewModel;
+ private final RecyclerView mTargetFeaturesView;
+ private final Observer<Integer> mSizeTypeObserver = this::onSizeTypeChanged;
+ private final Observer<List<AccessibilityTarget>> mTargetFeaturesObserver =
+ this::onTargetFeaturesChanged;
+ private final MenuViewAppearance mMenuViewAppearance;
+
+ MenuView(Context context, MenuViewModel menuViewModel, MenuViewAppearance menuViewAppearance) {
+ super(context);
+
+ mMenuViewModel = menuViewModel;
+ mMenuViewAppearance = menuViewAppearance;
+ mAdapter = new AccessibilityTargetAdapter(mTargetFeatures);
+ mTargetFeaturesView = new RecyclerView(context);
+ mTargetFeaturesView.setAdapter(mAdapter);
+ mTargetFeaturesView.setLayoutManager(new LinearLayoutManager(context));
+ setLayoutParams(new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ // Avoid drawing out of bounds of the parent view
+ setClipToOutline(true);
+ loadLayoutResources();
+
+ addView(mTargetFeaturesView);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ loadLayoutResources();
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private void onItemSizeChanged() {
+ mAdapter.setItemPadding(mMenuViewAppearance.getMenuPadding());
+ mAdapter.setIconWidthHeight(mMenuViewAppearance.getMenuIconSize());
+ mAdapter.notifyDataSetChanged();
+ }
+
+ private void onSizeChanged() {
+ final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) getLayoutParams();
+ layoutParams.height = mMenuViewAppearance.getMenuHeight();
+ setLayoutParams(layoutParams);
+ }
+
+ private void onEdgeChanged() {
+ final int[] insets = mMenuViewAppearance.getMenuInsets();
+ getContainerViewInsetLayer().setLayerInset(INDEX_MENU_ITEM, insets[0], insets[1], insets[2],
+ insets[3]);
+
+ final GradientDrawable gradientDrawable = getContainerViewGradient();
+ gradientDrawable.setCornerRadii(mMenuViewAppearance.getMenuRadii());
+ gradientDrawable.setStroke(mMenuViewAppearance.getMenuStrokeWidth(),
+ mMenuViewAppearance.getMenuStrokeColor());
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private void onSizeTypeChanged(int newSizeType) {
+ mMenuViewAppearance.setSizeType(newSizeType);
+
+ mAdapter.setItemPadding(mMenuViewAppearance.getMenuPadding());
+ mAdapter.setIconWidthHeight(mMenuViewAppearance.getMenuIconSize());
+ mAdapter.notifyDataSetChanged();
+
+ onSizeChanged();
+ onEdgeChanged();
+ }
+
+ private void onTargetFeaturesChanged(List<AccessibilityTarget> newTargetFeatures) {
+ // TODO(b/252756133): Should update specific item instead of the whole list
+ mTargetFeatures.clear();
+ mTargetFeatures.addAll(newTargetFeatures);
+ mMenuViewAppearance.setTargetFeaturesSize(mTargetFeatures.size());
+ mAdapter.notifyDataSetChanged();
+
+ onSizeChanged();
+ onEdgeChanged();
+ }
+
+ void show() {
+ mMenuViewModel.getTargetFeaturesData().observeForever(mTargetFeaturesObserver);
+ mMenuViewModel.getSizeTypeData().observeForever(mSizeTypeObserver);
+ setVisibility(VISIBLE);
+ mMenuViewModel.registerContentObservers();
+ }
+
+ void hide() {
+ setVisibility(GONE);
+ mMenuViewModel.getTargetFeaturesData().removeObserver(mTargetFeaturesObserver);
+ mMenuViewModel.getSizeTypeData().removeObserver(mSizeTypeObserver);
+ mMenuViewModel.unregisterContentObservers();
+ }
+
+ void loadLayoutResources() {
+ mMenuViewAppearance.update();
+
+ setBackground(mMenuViewAppearance.getMenuBackground());
+ setElevation(mMenuViewAppearance.getMenuElevation());
+ onItemSizeChanged();
+ onSizeChanged();
+ onEdgeChanged();
+ }
+
+ private InstantInsetLayerDrawable getContainerViewInsetLayer() {
+ return (InstantInsetLayerDrawable) getBackground();
+ }
+
+ private GradientDrawable getContainerViewGradient() {
+ return (GradientDrawable) getContainerViewInsetLayer().getDrawable(INDEX_MENU_ITEM);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
new file mode 100644
index 000000000000..b9b7732605c0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 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.accessibility.floatingmenu;
+
+import static com.android.systemui.accessibility.floatingmenu.MenuViewAppearance.MenuSizeType.SMALL;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.DimenRes;
+
+import com.android.systemui.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides the layout resources information of the {@link MenuView}.
+ */
+class MenuViewAppearance {
+ private final Resources mRes;
+ private int mTargetFeaturesSize;
+ private int mSizeType;
+ private int mSmallPadding;
+ private int mLargePadding;
+ private int mSmallIconSize;
+ private int mLargeIconSize;
+ private int mSmallSingleRadius;
+ private int mSmallMultipleRadius;
+ private int mLargeSingleRadius;
+ private int mLargeMultipleRadius;
+ private int mStrokeWidth;
+ private int mStrokeColor;
+ private int mInset;
+ private int mElevation;
+ private float[] mRadii;
+ private Drawable mBackgroundDrawable;
+
+ @IntDef({
+ SMALL,
+ MenuSizeType.LARGE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface MenuSizeType {
+ int SMALL = 0;
+ int LARGE = 1;
+ }
+
+ MenuViewAppearance(Context context) {
+ mRes = context.getResources();
+
+ update();
+ }
+
+ void update() {
+ mSmallPadding =
+ mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_small_padding);
+ mLargePadding =
+ mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_large_padding);
+ mSmallIconSize =
+ mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_small_width_height);
+ mLargeIconSize =
+ mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_large_width_height);
+ mSmallSingleRadius =
+ mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_small_single_radius);
+ mSmallMultipleRadius = mRes.getDimensionPixelSize(
+ R.dimen.accessibility_floating_menu_small_multiple_radius);
+ mRadii = createRadii(getMenuRadius(mTargetFeaturesSize));
+ mLargeSingleRadius =
+ mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_large_single_radius);
+ mLargeMultipleRadius = mRes.getDimensionPixelSize(
+ R.dimen.accessibility_floating_menu_large_multiple_radius);
+ mStrokeWidth = mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_stroke_width);
+ mStrokeColor = mRes.getColor(R.color.accessibility_floating_menu_stroke_dark);
+ mInset = mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_stroke_inset);
+ mElevation = mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation);
+ final Drawable drawable =
+ mRes.getDrawable(R.drawable.accessibility_floating_menu_background);
+ mBackgroundDrawable = new InstantInsetLayerDrawable(new Drawable[]{drawable});
+ }
+
+ void setSizeType(int sizeType) {
+ mSizeType = sizeType;
+
+ mRadii = createRadii(getMenuRadius(mTargetFeaturesSize));
+ }
+
+ void setTargetFeaturesSize(int targetFeaturesSize) {
+ mTargetFeaturesSize = targetFeaturesSize;
+
+ mRadii = createRadii(getMenuRadius(targetFeaturesSize));
+ }
+
+ Drawable getMenuBackground() {
+ return mBackgroundDrawable;
+ }
+
+ int getMenuElevation() {
+ return mElevation;
+ }
+
+ int getMenuHeight() {
+ return calculateActualMenuHeight();
+ }
+
+ int getMenuIconSize() {
+ return mSizeType == SMALL ? mSmallIconSize : mLargeIconSize;
+ }
+
+ int getMenuPadding() {
+ return mSizeType == SMALL ? mSmallPadding : mLargePadding;
+ }
+
+ int[] getMenuInsets() {
+ return new int[]{mInset, 0, 0, 0};
+ }
+
+ int getMenuStrokeWidth() {
+ return mStrokeWidth;
+ }
+
+ int getMenuStrokeColor() {
+ return mStrokeColor;
+ }
+
+ float[] getMenuRadii() {
+ return mRadii;
+ }
+
+ private int getMenuRadius(int itemCount) {
+ return mSizeType == SMALL ? getSmallSize(itemCount) : getLargeSize(itemCount);
+ }
+
+ @DimenRes
+ private int getSmallSize(int itemCount) {
+ return itemCount > 1 ? mSmallMultipleRadius : mSmallSingleRadius;
+ }
+
+ @DimenRes
+ private int getLargeSize(int itemCount) {
+ return itemCount > 1 ? mLargeMultipleRadius : mLargeSingleRadius;
+ }
+
+ private static float[] createRadii(float radius) {
+ return new float[]{0.0f, 0.0f, radius, radius, radius, radius, 0.0f, 0.0f};
+ }
+
+ private int calculateActualMenuHeight() {
+ final int menuPadding = getMenuPadding();
+
+ return (menuPadding + getMenuIconSize()) * mTargetFeaturesSize + menuPadding;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
new file mode 100644
index 000000000000..4ea2f7799c30
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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.accessibility.floatingmenu;
+
+import android.annotation.IntDef;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The basic interactions with the child view {@link MenuView}.
+ */
+@SuppressLint("ViewConstructor")
+class MenuViewLayer extends FrameLayout {
+ private final MenuView mMenuView;
+
+ @IntDef({
+ LayerIndex.MENU_VIEW
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface LayerIndex {
+ int MENU_VIEW = 0;
+ }
+
+ MenuViewLayer(@NonNull Context context) {
+ super(context);
+
+ final MenuViewModel menuViewModel = new MenuViewModel(context);
+ final MenuViewAppearance menuViewAppearance = new MenuViewAppearance(context);
+ mMenuView = new MenuView(context, menuViewModel, menuViewAppearance);
+
+ addView(mMenuView, LayerIndex.MENU_VIEW);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mMenuView.show();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ mMenuView.hide();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
new file mode 100644
index 000000000000..1e15a599f796
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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.accessibility.floatingmenu;
+
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.WindowManager;
+
+/**
+ * Controls the {@link MenuViewLayer} whether to be attached to the window via the interface
+ * of {@link IAccessibilityFloatingMenu}.
+ */
+class MenuViewLayerController implements IAccessibilityFloatingMenu {
+ private final WindowManager mWindowManager;
+ private final MenuViewLayer mMenuViewLayer;
+ private boolean mIsShowing;
+
+ MenuViewLayerController(Context context, WindowManager windowManager) {
+ mWindowManager = windowManager;
+ mMenuViewLayer = new MenuViewLayer(context);
+ }
+
+ @Override
+ public boolean isShowing() {
+ return mIsShowing;
+ }
+
+ @Override
+ public void show() {
+ if (isShowing()) {
+ return;
+ }
+
+ mIsShowing = true;
+ mWindowManager.addView(mMenuViewLayer, createDefaultLayerLayoutParams());
+ }
+
+ @Override
+ public void hide() {
+ if (!isShowing()) {
+ return;
+ }
+
+ mIsShowing = false;
+ mWindowManager.removeView(mMenuViewLayer);
+ }
+
+ private static WindowManager.LayoutParams createDefaultLayerLayoutParams() {
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ params.privateFlags |= PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
+ params.windowAnimations = android.R.style.Animation_Translucent;
+
+ return params;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
new file mode 100644
index 000000000000..c3ba43950b6e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 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.accessibility.floatingmenu;
+
+import android.content.Context;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+
+import com.android.internal.accessibility.dialog.AccessibilityTarget;
+
+import java.util.List;
+
+/**
+ * The view model provides the menu information from the repository{@link MenuInfoRepository} for
+ * the menu view{@link MenuView}.
+ */
+class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged {
+ private final MutableLiveData<List<AccessibilityTarget>> mTargetFeaturesData =
+ new MutableLiveData<>();
+ private final MutableLiveData<Integer> mSizeTypeData = new MutableLiveData<>();
+ private final MenuInfoRepository mInfoRepository;
+
+ MenuViewModel(Context context) {
+ mInfoRepository = new MenuInfoRepository(context, /* settingsContentsChanged= */ this);
+ }
+
+ @Override
+ public void onTargetFeaturesChanged(List<AccessibilityTarget> newTargetFeatures) {
+ mTargetFeaturesData.setValue(newTargetFeatures);
+ }
+
+ @Override
+ public void onSizeTypeChanged(int newSizeType) {
+ mSizeTypeData.setValue(newSizeType);
+ }
+
+ LiveData<Integer> getSizeTypeData() {
+ mInfoRepository.loadMenuSizeType(mSizeTypeData::setValue);
+ return mSizeTypeData;
+ }
+
+ LiveData<List<AccessibilityTarget>> getTargetFeaturesData() {
+ mInfoRepository.loadMenuTargetFeatures(mTargetFeaturesData::setValue);
+ return mTargetFeaturesData;
+ }
+
+ void registerContentObservers() {
+ mInfoRepository.registerContentObservers();
+ }
+
+ void unregisterContentObservers() {
+ mInfoRepository.unregisterContentObservers();
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 2cca086571c0..3e796cd0c879 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -485,45 +485,18 @@ public class AuthContainerView extends LinearLayout
mContainerState = STATE_SHOWING;
} else {
mContainerState = STATE_ANIMATING_IN;
- // The background panel and content are different views since we need to be able to
- // animate them separately in other places.
- mPanelView.setY(mTranslationY);
- mBiometricScrollView.setY(mTranslationY);
-
+ setY(mTranslationY);
setAlpha(0f);
final long animateDuration = mConfig.mSkipAnimation ? 0 : ANIMATION_DURATION_SHOW_MS;
postOnAnimation(() -> {
- mPanelView.animate()
- .translationY(0)
- .setDuration(animateDuration)
- .setInterpolator(mLinearOutSlowIn)
- .setListener(getJankListener(mPanelView, SHOW, animateDuration))
- .withLayer()
- .withEndAction(this::onDialogAnimatedIn)
- .start();
- mBiometricScrollView.animate()
- .translationY(0)
- .setDuration(animateDuration)
- .setInterpolator(mLinearOutSlowIn)
- .setListener(getJankListener(mBiometricScrollView, SHOW, animateDuration))
- .withLayer()
- .start();
- if (mCredentialView != null && mCredentialView.isAttachedToWindow()) {
- mCredentialView.setY(mTranslationY);
- mCredentialView.animate()
- .translationY(0)
- .setDuration(animateDuration)
- .setInterpolator(mLinearOutSlowIn)
- .setListener(getJankListener(mCredentialView, SHOW, animateDuration))
- .withLayer()
- .start();
- }
animate()
.alpha(1f)
+ .translationY(0)
.setDuration(animateDuration)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.setListener(getJankListener(this, SHOW, animateDuration))
+ .withEndAction(this::onDialogAnimatedIn)
.start();
});
}
@@ -794,32 +767,9 @@ public class AuthContainerView extends LinearLayout
final long animateDuration = mConfig.mSkipAnimation ? 0 : ANIMATION_DURATION_AWAY_MS;
postOnAnimation(() -> {
- mPanelView.animate()
- .translationY(mTranslationY)
- .setDuration(animateDuration)
- .setInterpolator(mLinearOutSlowIn)
- .setListener(getJankListener(mPanelView, DISMISS, animateDuration))
- .withLayer()
- .withEndAction(endActionRunnable)
- .start();
- mBiometricScrollView.animate()
- .translationY(mTranslationY)
- .setDuration(animateDuration)
- .setInterpolator(mLinearOutSlowIn)
- .setListener(getJankListener(mBiometricScrollView, DISMISS, animateDuration))
- .withLayer()
- .start();
- if (mCredentialView != null && mCredentialView.isAttachedToWindow()) {
- mCredentialView.animate()
- .translationY(mTranslationY)
- .setDuration(animateDuration)
- .setInterpolator(mLinearOutSlowIn)
- .setListener(getJankListener(mCredentialView, DISMISS, animateDuration))
- .withLayer()
- .start();
- }
animate()
.alpha(0f)
+ .translationY(mTranslationY)
.setDuration(animateDuration)
.setInterpolator(mLinearOutSlowIn)
.setListener(getJankListener(this, DISMISS, animateDuration))
@@ -834,6 +784,7 @@ public class AuthContainerView extends LinearLayout
mWindowManager.updateViewLayout(this, lp);
})
.withLayer()
+ .withEndAction(endActionRunnable)
.start();
});
}
@@ -855,7 +806,7 @@ public class AuthContainerView extends LinearLayout
}
mContainerState = STATE_GONE;
if (isAttachedToWindow()) {
- mWindowManager.removeView(this);
+ mWindowManager.removeViewImmediate(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 1ceb6b3ca172..d1bc968a5be6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -143,6 +143,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@Nullable private IUdfpsHbmListener mUdfpsHbmListener;
@Nullable private SidefpsController mSidefpsController;
@Nullable private IBiometricContextListener mBiometricContextListener;
+ @Nullable private UdfpsLogger mUdfpsLogger;
@VisibleForTesting IBiometricSysuiReceiver mReceiver;
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
@Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
@@ -289,6 +290,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
});
mUdfpsController.setAuthControllerUpdateUdfpsLocation(this::updateUdfpsLocation);
+ mUdfpsController.setUdfpsDisplayMode(new UdfpsDisplayMode(mContext, mExecution,
+ this, mUdfpsLogger));
mUdfpsBounds = mUdfpsProps.get(0).getLocation().getRect();
}
@@ -688,6 +691,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@NonNull WakefulnessLifecycle wakefulnessLifecycle,
@NonNull UserManager userManager,
@NonNull LockPatternUtils lockPatternUtils,
+ @NonNull UdfpsLogger udfpsLogger,
@NonNull StatusBarStateController statusBarStateController,
@NonNull InteractionJankMonitor jankMonitor,
@Main Handler handler,
@@ -705,6 +709,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mFaceManager = faceManager;
mUdfpsControllerFactory = udfpsControllerFactory;
mSidefpsControllerFactory = sidefpsControllerFactory;
+ mUdfpsLogger = udfpsLogger;
mDisplayManager = displayManager;
mWindowManager = windowManager;
mInteractionJankMonitor = jankMonitor;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index f6dedd95198b..2578df33c167 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -123,7 +123,6 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
@NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- @Nullable private final UdfpsDisplayModeProvider mUdfpsDisplayMode;
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final SystemClock mSystemClock;
@NonNull private final UnlockedScreenOffAnimationController
@@ -139,6 +138,7 @@ public class UdfpsController implements DozeReceiver {
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
@Nullable private Runnable mAuthControllerUpdateUdfpsLocation;
@Nullable private final AlternateUdfpsTouchProvider mAlternateTouchProvider;
+ @Nullable private UdfpsDisplayModeProvider mUdfpsDisplayMode;
// Tracks the velocity of a touch to help filter out the touches that move too fast.
@Nullable private VelocityTracker mVelocityTracker;
@@ -319,6 +319,10 @@ public class UdfpsController implements DozeReceiver {
mAuthControllerUpdateUdfpsLocation = r;
}
+ public void setUdfpsDisplayMode(UdfpsDisplayModeProvider udfpsDisplayMode) {
+ mUdfpsDisplayMode = udfpsDisplayMode;
+ }
+
/**
* Calculate the pointer speed given a velocity tracker and the pointer id.
* This assumes that the velocity tracker has already been passed all relevant motion events.
@@ -594,7 +598,6 @@ public class UdfpsController implements DozeReceiver {
@NonNull VibratorHelper vibrator,
@NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
@NonNull UdfpsShell udfpsShell,
- @NonNull Optional<UdfpsDisplayModeProvider> udfpsDisplayMode,
@NonNull KeyguardStateController keyguardStateController,
@NonNull DisplayManager displayManager,
@Main Handler mainHandler,
@@ -626,7 +629,6 @@ public class UdfpsController implements DozeReceiver {
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
- mUdfpsDisplayMode = udfpsDisplayMode.orElse(null);
screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
mConfigurationController = configurationController;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayMode.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayMode.kt
new file mode 100644
index 000000000000..b80b8a0ff219
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayMode.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.os.RemoteException
+import android.os.Trace
+import com.android.systemui.util.concurrency.Execution
+
+private const val TAG = "UdfpsDisplayMode"
+
+/**
+ * UdfpsDisplayMode configures the display for optimal UDFPS operation. For example, sets the
+ * display refresh rate that's optimal for UDFPS.
+ */
+class UdfpsDisplayMode
+constructor(
+ private val context: Context,
+ private val execution: Execution,
+ private val authController: AuthController,
+ private val logger: UdfpsLogger
+) : UdfpsDisplayModeProvider {
+
+ // The request is reset to null after it's processed.
+ private var currentRequest: Request? = null
+
+ override fun enable(onEnabled: Runnable?) {
+ execution.isMainThread()
+ logger.v(TAG, "enable")
+
+ if (currentRequest != null) {
+ logger.e(TAG, "enable | already requested")
+ return
+ }
+ if (authController.udfpsHbmListener == null) {
+ logger.e(TAG, "enable | mDisplayManagerCallback is null")
+ return
+ }
+
+ Trace.beginSection("UdfpsDisplayMode.enable")
+
+ // Track this request in one object.
+ val request = Request(context.displayId)
+ currentRequest = request
+
+ try {
+ // This method is a misnomer. It has nothing to do with HBM, its purpose is to set
+ // the appropriate display refresh rate.
+ authController.udfpsHbmListener!!.onHbmEnabled(request.displayId)
+ logger.v(TAG, "enable | requested optimal refresh rate for UDFPS")
+ } catch (e: RemoteException) {
+ logger.e(TAG, "enable", e)
+ }
+
+ onEnabled?.run() ?: logger.w(TAG, "enable | onEnabled is null")
+ Trace.endSection()
+ }
+
+ override fun disable(onDisabled: Runnable?) {
+ execution.isMainThread()
+ logger.v(TAG, "disable")
+
+ val request = currentRequest
+ if (request == null) {
+ logger.w(TAG, "disable | already disabled")
+ return
+ }
+
+ Trace.beginSection("UdfpsDisplayMode.disable")
+
+ try {
+ // Allow DisplayManager to unset the UDFPS refresh rate.
+ authController.udfpsHbmListener!!.onHbmDisabled(request.displayId)
+ logger.v(TAG, "disable | removed the UDFPS refresh rate request")
+ } catch (e: RemoteException) {
+ logger.e(TAG, "disable", e)
+ }
+
+ currentRequest = null
+ onDisabled?.run() ?: logger.w(TAG, "disable | onDisabled is null")
+ Trace.endSection()
+ }
+}
+
+/** Tracks a request to enable the UDFPS mode. */
+private data class Request(val displayId: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 934aedf98a91..4d7f89d7b727 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -219,7 +219,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
mView.animateInUdfpsBouncer(null);
}
- if (mKeyguardViewManager.isOccluded()) {
+ if (mKeyguardStateController.isOccluded()) {
mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt
new file mode 100644
index 000000000000..39199d194cc9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.LogLevel.ERROR
+import com.android.systemui.log.LogLevel.VERBOSE
+import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.dagger.UdfpsLog
+import com.google.errorprone.annotations.CompileTimeConstant
+import javax.inject.Inject
+
+private const val TAG = "UdfpsLogger"
+
+/** Helper class for logging for Udfps */
+class UdfpsLogger @Inject constructor(@UdfpsLog private val logBuffer: LogBuffer) {
+ fun e(tag: String, @CompileTimeConstant msg: String) = log(tag, msg, ERROR)
+
+ fun e(tag: String, @CompileTimeConstant msg: String, throwable: Throwable?) {
+ logBuffer.log(tag, ERROR, {}, { msg }, exception = throwable)
+ }
+
+ fun v(tag: String, @CompileTimeConstant msg: String) = log(tag, msg, VERBOSE)
+
+ fun w(tag: String, @CompileTimeConstant msg: String) = log(tag, msg, WARNING)
+
+ fun log(tag: String, @CompileTimeConstant msg: String, level: LogLevel) {
+ logBuffer.log(tag, level, msg)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
new file mode 100644
index 000000000000..f95a8ee89a2c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 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.common.ui.view
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.ImageView
+import com.android.systemui.animation.LaunchableView
+import com.android.systemui.animation.LaunchableViewDelegate
+
+class LaunchableImageView : ImageView, LaunchableView {
+ private val delegate =
+ LaunchableViewDelegate(
+ this,
+ superSetVisibility = { super.setVisibility(it) },
+ superSetTransitionVisibility = { super.setTransitionVisibility(it) },
+ )
+
+ constructor(context: Context?) : super(context)
+ constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
+ constructor(
+ context: Context?,
+ attrs: AttributeSet?,
+ defStyleAttr: Int,
+ ) : super(context, attrs, defStyleAttr)
+
+ constructor(
+ context: Context?,
+ attrs: AttributeSet?,
+ defStyleAttr: Int,
+ defStyleRes: Int,
+ ) : super(context, attrs, defStyleAttr, defStyleRes)
+
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {
+ delegate.setShouldBlockVisibilityChanges(block)
+ }
+
+ override fun setVisibility(visibility: Int) {
+ delegate.setVisibility(visibility)
+ }
+
+ override fun setTransitionVisibility(visibility: Int) {
+ delegate.setTransitionVisibility(visibility)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index a99669970a34..48bef97c30fb 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -22,25 +22,19 @@ import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
import android.content.Context;
import android.hardware.SensorPrivacyManager;
import android.os.Handler;
-import android.os.PowerManager;
import androidx.annotation.Nullable;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardViewController;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.dagger.MediaModule;
import com.android.systemui.navigationbar.gestural.GestureModule;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.dagger.PowerModule;
import com.android.systemui.qs.dagger.QSModule;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -62,8 +56,7 @@ import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryControllerImpl;
+import com.android.systemui.statusbar.policy.AospPolicyModule;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
@@ -97,6 +90,7 @@ import dagger.Provides;
* SystemUI code that variants of SystemUI _must_ include to function correctly.
*/
@Module(includes = {
+ AospPolicyModule.class,
GestureModule.class,
MediaModule.class,
PowerModule.class,
@@ -121,30 +115,6 @@ public abstract class ReferenceSystemUIModule {
@Provides
@SysUISingleton
- static BatteryController provideBatteryController(
- Context context,
- EnhancedEstimates enhancedEstimates,
- PowerManager powerManager,
- BroadcastDispatcher broadcastDispatcher,
- DemoModeController demoModeController,
- DumpManager dumpManager,
- @Main Handler mainHandler,
- @Background Handler bgHandler) {
- BatteryController bC = new BatteryControllerImpl(
- context,
- enhancedEstimates,
- powerManager,
- broadcastDispatcher,
- demoModeController,
- dumpManager,
- mainHandler,
- bgHandler);
- bC.init();
- return bC;
- }
-
- @Provides
- @SysUISingleton
static SensorPrivacyController provideSensorPrivacyController(
SensorPrivacyManager sensorPrivacyManager) {
SensorPrivacyController spC = new SensorPrivacyControllerImpl(sensorPrivacyManager);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 0d06c513d248..c2dffe8bdad0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -27,9 +27,6 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionCli;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
-import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
-import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
-import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
import com.android.systemui.people.PeopleProvider;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.unfold.FoldStateLogger;
@@ -133,9 +130,6 @@ public interface SysUIComponent {
});
getNaturalRotationUnfoldProgressProvider().ifPresent(o -> o.init());
// No init method needed, just needs to be gotten so that it's created.
- getMediaTttChipControllerSender();
- getMediaTttChipControllerReceiver();
- getMediaTttCommandLineHelper();
getMediaMuteAwaitConnectionCli();
getNearbyMediaDevicesManager();
getUnfoldLatencyTracker().init();
@@ -206,15 +200,6 @@ public interface SysUIComponent {
Optional<NaturalRotationUnfoldProgressProvider> getNaturalRotationUnfoldProgressProvider();
/** */
- Optional<MediaTttChipControllerSender> getMediaTttChipControllerSender();
-
- /** */
- Optional<MediaTttChipControllerReceiver> getMediaTttChipControllerReceiver();
-
- /** */
- Optional<MediaTttCommandLineHelper> getMediaTttCommandLineHelper();
-
- /** */
Optional<MediaMuteAwaitConnectionCli> getMediaMuteAwaitConnectionCli();
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 8bb27a7bc217..55eda0a0cb8a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -32,6 +32,9 @@ import com.android.systemui.keyboard.KeyboardUI
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.log.SessionTracker
import com.android.systemui.media.RingtonePlayer
+import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper
+import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
+import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
import com.android.systemui.power.PowerUI
import com.android.systemui.recents.Recents
import com.android.systemui.settings.dagger.MultiUserUtilsModule
@@ -213,4 +216,26 @@ abstract class SystemUICoreStartableModule {
@IntoMap
@ClassKey(KeyguardLiftController::class)
abstract fun bindKeyguardLiftController(sysui: KeyguardLiftController): CoreStartable
+
+ /** Inject into MediaTttChipControllerReceiver. */
+ @Binds
+ @IntoMap
+ @ClassKey(MediaTttChipControllerReceiver::class)
+ abstract fun bindMediaTttChipControllerReceiver(
+ sysui: MediaTttChipControllerReceiver
+ ): CoreStartable
+
+ /** Inject into MediaTttChipControllerSender. */
+ @Binds
+ @IntoMap
+ @ClassKey(MediaTttChipControllerSender::class)
+ abstract fun bindMediaTttChipControllerSender(
+ sysui: MediaTttChipControllerSender
+ ): CoreStartable
+
+ /** Inject into MediaTttCommandLineHelper. */
+ @Binds
+ @IntoMap
+ @ClassKey(MediaTttCommandLineHelper::class)
+ abstract fun bindMediaTttCommandLineHelper(sysui: MediaTttCommandLineHelper): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
index dfa3bcda7d72..fb4fc928c8dc 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
@@ -16,12 +16,14 @@
package com.android.systemui.flags
+import android.util.Dumpable
+
/**
* Class to manage simple DeviceConfig-based feature flags.
*
* See [Flags] for instructions on defining new flags.
*/
-interface FeatureFlags : FlagListenable {
+interface FeatureFlags : FlagListenable, Dumpable {
/** Returns a boolean value for the given flag. */
fun isEnabled(flag: UnreleasedFlag): Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index b4b87952980b..3adeeac2e4d4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -30,7 +30,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.Log;
@@ -38,22 +37,15 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.commandline.Command;
-import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.settings.SecureSettings;
import org.jetbrains.annotations.NotNull;
import java.io.PrintWriter;
-import java.lang.reflect.Field;
import java.util.ArrayList;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
@@ -76,10 +68,9 @@ import javax.inject.Named;
* To restore a flag back to its default, leave the `--ez value <0|1>` off of the command.
*/
@SysUISingleton
-public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
- private static final String TAG = "SysUIFlags";
+public class FeatureFlagsDebug implements FeatureFlags {
+ static final String TAG = "SysUIFlags";
static final String ALL_FLAGS = "all_flags";
- private static final String FLAG_COMMAND = "flag";
private final FlagManager mFlagManager;
private final SecureSettings mSecureSettings;
@@ -90,7 +81,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
private final Map<Integer, Flag<?>> mAllFlags;
private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
- private final IStatusBarService mBarService;
+ private final Restarter mRestarter;
@Inject
public FeatureFlagsDebug(
@@ -99,12 +90,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
SecureSettings secureSettings,
SystemPropertiesHelper systemProperties,
@Main Resources resources,
- DumpManager dumpManager,
DeviceConfigProxy deviceConfigProxy,
ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
- CommandRegistry commandRegistry,
- IStatusBarService barService) {
+ Restarter barService) {
mFlagManager = flagManager;
mSecureSettings = secureSettings;
mResources = resources;
@@ -112,7 +101,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
mDeviceConfigProxy = deviceConfigProxy;
mServerFlagReader = serverFlagReader;
mAllFlags = allFlags;
- mBarService = barService;
+ mRestarter = barService;
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_SET_FLAG);
@@ -121,8 +110,6 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
flagManager.setClearCacheAction(this::removeFromCache);
context.registerReceiver(mReceiver, filter, null, null,
Context.RECEIVER_EXPORTED_UNAUDITED);
- dumpManager.registerDumpable(TAG, this);
- commandRegistry.registerCommand(FLAG_COMMAND, FlagCommand::new);
}
@Override
@@ -267,7 +254,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
mFlagManager.dispatchListenersAndMaybeRestart(id, this::restartSystemUI);
}
- private <T> void eraseFlag(Flag<T> flag) {
+ <T> void eraseFlag(Flag<T> flag) {
if (flag instanceof SysPropFlag) {
mSystemProperties.erase(((SysPropFlag<T>) flag).getName());
dispatchListenersAndMaybeRestart(flag.getId(), this::restartAndroid);
@@ -320,13 +307,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
return;
}
Log.i(TAG, "Restarting Android");
- try {
- mBarService.restart();
- } catch (RemoteException e) {
- }
+ mRestarter.restart();
}
- private void setBooleanFlagInternal(Flag<?> flag, boolean value) {
+ void setBooleanFlagInternal(Flag<?> flag, boolean value) {
if (flag instanceof BooleanFlag) {
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
} else if (flag instanceof ResourceBooleanFlag) {
@@ -343,7 +327,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
}
}
- private void setStringFlagInternal(Flag<?> flag, String value) {
+ void setStringFlagInternal(Flag<?> flag, String value) {
if (flag instanceof StringFlag) {
setFlagValue(flag.getId(), value, StringFlagSerializer.INSTANCE);
} else if (flag instanceof ResourceStringFlag) {
@@ -477,154 +461,4 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
+ ": [length=" + value.length() + "] \"" + value + "\""));
}
- class FlagCommand implements Command {
- private final List<String> mOnCommands = List.of("true", "on", "1", "enabled");
- private final List<String> mOffCommands = List.of("false", "off", "0", "disable");
-
- @Override
- public void execute(@NonNull PrintWriter pw, @NonNull List<String> args) {
- if (args.size() == 0) {
- pw.println("Error: no flag id supplied");
- help(pw);
- pw.println();
- printKnownFlags(pw);
- return;
- }
-
- if (args.size() > 2) {
- pw.println("Invalid number of arguments.");
- help(pw);
- return;
- }
-
- int id = 0;
- try {
- id = Integer.parseInt(args.get(0));
- if (!mAllFlags.containsKey(id)) {
- pw.println("Unknown flag id: " + id);
- pw.println();
- printKnownFlags(pw);
- return;
- }
- } catch (NumberFormatException e) {
- id = flagNameToId(args.get(0));
- if (id == 0) {
- pw.println("Invalid flag. Must an integer id or flag name: " + args.get(0));
- return;
- }
- }
- Flag<?> flag = mAllFlags.get(id);
-
- String cmd = "";
- if (args.size() == 2) {
- cmd = args.get(1).toLowerCase();
- }
-
- if ("erase".equals(cmd) || "reset".equals(cmd)) {
- eraseFlag(flag);
- return;
- }
-
- boolean newValue = true;
- if (args.size() == 1 || "toggle".equals(cmd)) {
- boolean enabled = isBooleanFlagEnabled(flag);
-
- if (args.size() == 1) {
- pw.println("Flag " + id + " is " + enabled);
- return;
- }
-
- newValue = !enabled;
- } else {
- newValue = mOnCommands.contains(cmd);
- if (!newValue && !mOffCommands.contains(cmd)) {
- pw.println("Invalid on/off argument supplied");
- help(pw);
- return;
- }
- }
-
- pw.flush(); // Next command will restart sysui, so flush before we do so.
- setBooleanFlagInternal(flag, newValue);
- }
-
- @Override
- public void help(PrintWriter pw) {
- pw.println(
- "Usage: adb shell cmd statusbar flag <id> "
- + "[true|false|1|0|on|off|enable|disable|toggle|erase|reset]");
- pw.println("The id can either be a numeric integer or the corresponding field name");
- pw.println(
- "If no argument is supplied after the id, the flags runtime value is output");
- }
-
- private boolean isBooleanFlagEnabled(Flag<?> flag) {
- if (flag instanceof ReleasedFlag) {
- return isEnabled((ReleasedFlag) flag);
- } else if (flag instanceof UnreleasedFlag) {
- return isEnabled((UnreleasedFlag) flag);
- } else if (flag instanceof ResourceBooleanFlag) {
- return isEnabled((ResourceBooleanFlag) flag);
- } else if (flag instanceof SysPropFlag) {
- return isEnabled((SysPropBooleanFlag) flag);
- }
-
- return false;
- }
-
- private int flagNameToId(String flagName) {
- List<Field> fields = Flags.getFlagFields();
- for (Field field : fields) {
- if (flagName.equals(field.getName())) {
- return fieldToId(field);
- }
- }
-
- return 0;
- }
-
- private int fieldToId(Field field) {
- try {
- Flag<?> flag = (Flag<?>) field.get(null);
- return flag.getId();
- } catch (IllegalAccessException e) {
- // no-op
- }
-
- return 0;
- }
-
- private void printKnownFlags(PrintWriter pw) {
- List<Field> fields = Flags.getFlagFields();
-
- int longestFieldName = 0;
- for (Field field : fields) {
- longestFieldName = Math.max(longestFieldName, field.getName().length());
- }
-
- pw.println("Known Flags:");
- pw.print("Flag Name");
- for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) {
- pw.print(" ");
- }
- pw.println("ID Enabled?");
- for (int i = 0; i < longestFieldName; i++) {
- pw.print("=");
- }
- pw.println(" ==== ========");
- for (Field field : fields) {
- int id = fieldToId(field);
- if (id == 0 || !mAllFlags.containsKey(id)) {
- continue;
- }
- pw.print(field.getName());
- int fieldWidth = field.getName().length();
- for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) {
- pw.print(" ");
- }
- pw.printf("%-4d ", id);
- pw.println(isBooleanFlagEnabled(mAllFlags.get(id)));
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
new file mode 100644
index 000000000000..c0e30211e018
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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.flags
+
+import android.content.Context
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import javax.inject.Inject
+
+class FeatureFlagsDebugStartable
+@Inject
+constructor(
+ @Application context: Context,
+ dumpManager: DumpManager,
+ private val commandRegistry: CommandRegistry,
+ private val flagCommand: FlagCommand,
+ featureFlags: FeatureFlags
+) : CoreStartable(context) {
+
+ init {
+ dumpManager.registerDumpable(FeatureFlagsDebug.TAG) { pw, args ->
+ featureFlags.dump(pw, args)
+ }
+ }
+
+ override fun start() {
+ commandRegistry.registerCommand(FlagCommand.FLAG_COMMAND) { flagCommand }
+ }
+}
+
+@Module
+abstract class FeatureFlagsDebugStartableModule {
+ @Binds
+ @IntoMap
+ @ClassKey(FeatureFlagsDebugStartable::class)
+ abstract fun bind(impl: FeatureFlagsDebugStartable): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 049b17d383a2..40a8a1a9ef01 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -24,10 +24,8 @@ import android.util.SparseBooleanArray;
import androidx.annotation.NonNull;
-import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.DeviceConfigProxy;
import org.jetbrains.annotations.NotNull;
@@ -44,27 +42,26 @@ import javax.inject.Inject;
* how to set flags.
*/
@SysUISingleton
-public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
+public class FeatureFlagsRelease implements FeatureFlags {
+ static final String TAG = "SysUIFlags";
+
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
private final DeviceConfigProxy mDeviceConfigProxy;
private final ServerFlagReader mServerFlagReader;
SparseBooleanArray mBooleanCache = new SparseBooleanArray();
SparseArray<String> mStringCache = new SparseArray<>();
- private boolean mInited;
@Inject
public FeatureFlagsRelease(
@Main Resources resources,
SystemPropertiesHelper systemProperties,
DeviceConfigProxy deviceConfigProxy,
- ServerFlagReader serverFlagReader,
- DumpManager dumpManager) {
+ ServerFlagReader serverFlagReader) {
mResources = resources;
mSystemProperties = systemProperties;
mDeviceConfigProxy = deviceConfigProxy;
mServerFlagReader = serverFlagReader;
- dumpManager.registerDumpable("SysUIFlags", this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
new file mode 100644
index 000000000000..f138f1e8aa79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 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.flags
+
+import android.content.Context
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dump.DumpManager
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import javax.inject.Inject
+
+class FeatureFlagsReleaseStartable
+@Inject
+constructor(@Application context: Context, dumpManager: DumpManager, featureFlags: FeatureFlags) :
+ CoreStartable(context) {
+
+ init {
+ dumpManager.registerDumpable(FeatureFlagsRelease.TAG) { pw, args ->
+ featureFlags.dump(pw, args)
+ }
+ }
+
+ override fun start() {
+ // no-op
+ }
+}
+
+@Module
+abstract class FeatureFlagsReleaseStartableModule {
+ @Binds
+ @IntoMap
+ @ClassKey(FeatureFlagsReleaseStartable::class)
+ abstract fun bind(impl: FeatureFlagsReleaseStartable): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
new file mode 100644
index 000000000000..4d254313a57b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2022 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.flags;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.statusbar.commandline.Command;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * A {@link Command} used to flip flags in SystemUI.
+ */
+public class FlagCommand implements Command {
+ public static final String FLAG_COMMAND = "flag";
+
+ private final List<String> mOnCommands = List.of("true", "on", "1", "enabled");
+ private final List<String> mOffCommands = List.of("false", "off", "0", "disable");
+ private final FeatureFlagsDebug mFeatureFlags;
+ private final Map<Integer, Flag<?>> mAllFlags;
+
+ @Inject
+ FlagCommand(
+ FeatureFlagsDebug featureFlags,
+ @Named(FeatureFlagsDebug.ALL_FLAGS) Map<Integer, Flag<?>> allFlags
+ ) {
+ mFeatureFlags = featureFlags;
+ mAllFlags = allFlags;
+ }
+
+ @Override
+ public void execute(@NonNull PrintWriter pw, @NonNull List<String> args) {
+ if (args.size() == 0) {
+ pw.println("Error: no flag id supplied");
+ help(pw);
+ pw.println();
+ printKnownFlags(pw);
+ return;
+ }
+
+ if (args.size() > 2) {
+ pw.println("Invalid number of arguments.");
+ help(pw);
+ return;
+ }
+
+ int id = 0;
+ try {
+ id = Integer.parseInt(args.get(0));
+ if (!mAllFlags.containsKey(id)) {
+ pw.println("Unknown flag id: " + id);
+ pw.println();
+ printKnownFlags(pw);
+ return;
+ }
+ } catch (NumberFormatException e) {
+ id = flagNameToId(args.get(0));
+ if (id == 0) {
+ pw.println("Invalid flag. Must an integer id or flag name: " + args.get(0));
+ return;
+ }
+ }
+ Flag<?> flag = mAllFlags.get(id);
+
+ String cmd = "";
+ if (args.size() == 2) {
+ cmd = args.get(1).toLowerCase();
+ }
+
+ if ("erase".equals(cmd) || "reset".equals(cmd)) {
+ mFeatureFlags.eraseFlag(flag);
+ return;
+ }
+
+ boolean newValue = true;
+ if (args.size() == 1 || "toggle".equals(cmd)) {
+ boolean enabled = isBooleanFlagEnabled(flag);
+
+ if (args.size() == 1) {
+ pw.println("Flag " + id + " is " + enabled);
+ return;
+ }
+
+ newValue = !enabled;
+ } else {
+ newValue = mOnCommands.contains(cmd);
+ if (!newValue && !mOffCommands.contains(cmd)) {
+ pw.println("Invalid on/off argument supplied");
+ help(pw);
+ return;
+ }
+ }
+
+ pw.flush(); // Next command will restart sysui, so flush before we do so.
+ mFeatureFlags.setBooleanFlagInternal(flag, newValue);
+ }
+
+ @Override
+ public void help(PrintWriter pw) {
+ pw.println(
+ "Usage: adb shell cmd statusbar flag <id> "
+ + "[true|false|1|0|on|off|enable|disable|toggle|erase|reset]");
+ pw.println("The id can either be a numeric integer or the corresponding field name");
+ pw.println(
+ "If no argument is supplied after the id, the flags runtime value is output");
+ }
+
+ private boolean isBooleanFlagEnabled(Flag<?> flag) {
+ if (flag instanceof ReleasedFlag) {
+ return mFeatureFlags.isEnabled((ReleasedFlag) flag);
+ } else if (flag instanceof UnreleasedFlag) {
+ return mFeatureFlags.isEnabled((UnreleasedFlag) flag);
+ } else if (flag instanceof ResourceBooleanFlag) {
+ return mFeatureFlags.isEnabled((ResourceBooleanFlag) flag);
+ } else if (flag instanceof SysPropFlag) {
+ return mFeatureFlags.isEnabled((SysPropBooleanFlag) flag);
+ }
+
+ return false;
+ }
+
+ private int flagNameToId(String flagName) {
+ List<Field> fields = Flags.getFlagFields();
+ for (Field field : fields) {
+ if (flagName.equals(field.getName())) {
+ return fieldToId(field);
+ }
+ }
+
+ return 0;
+ }
+
+ private int fieldToId(Field field) {
+ try {
+ Flag<?> flag = (Flag<?>) field.get(null);
+ return flag.getId();
+ } catch (IllegalAccessException e) {
+ // no-op
+ }
+
+ return 0;
+ }
+
+ private void printKnownFlags(PrintWriter pw) {
+ List<Field> fields = Flags.getFlagFields();
+
+ int longestFieldName = 0;
+ for (Field field : fields) {
+ longestFieldName = Math.max(longestFieldName, field.getName().length());
+ }
+
+ pw.println("Known Flags:");
+ pw.print("Flag Name");
+ for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) {
+ pw.print(" ");
+ }
+ pw.println("ID Enabled?");
+ for (int i = 0; i < longestFieldName; i++) {
+ pw.print("=");
+ }
+ pw.println(" ==== ========");
+ for (Field field : fields) {
+ int id = fieldToId(field);
+ if (id == 0 || !mAllFlags.containsKey(id)) {
+ continue;
+ }
+ pw.print(field.getName());
+ int fieldWidth = field.getName().length();
+ for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) {
+ pw.print(" ");
+ }
+ pw.printf("%-4d ", id);
+ pw.println(isBooleanFlagEnabled(mAllFlags.get(id)));
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index aa57175420ef..3dbadb0f07c9 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -116,7 +116,7 @@ public class Flags {
* the framework APIs.
*/
public static final UnreleasedFlag USER_INTERACTOR_AND_REPO_USE_CONTROLLER =
- new UnreleasedFlag(210, true);
+ new UnreleasedFlag(210);
/**
* Whether `UserSwitcherController` should use the user interactor.
@@ -127,8 +127,7 @@ public class Flags {
* <p>Note: do not set this to true if {@link #USER_INTERACTOR_AND_REPO_USE_CONTROLLER} is
* {@code true} as it would created a cycle between controller -> interactor -> controller.
*/
- public static final UnreleasedFlag USER_CONTROLLER_USES_INTERACTOR =
- new UnreleasedFlag(211, false);
+ public static final ReleasedFlag USER_CONTROLLER_USES_INTERACTOR = new ReleasedFlag(211);
/***************************************/
// 300 - power menu
@@ -174,7 +173,7 @@ public class Flags {
public static final ResourceBooleanFlag FULL_SCREEN_USER_SWITCHER =
new ResourceBooleanFlag(506, R.bool.config_enableFullscreenUserSwitcher);
- public static final UnreleasedFlag NEW_FOOTER_ACTIONS = new UnreleasedFlag(507, true);
+ public static final ReleasedFlag NEW_FOOTER_ACTIONS = new ReleasedFlag(507);
/***************************************/
// 600- status bar
@@ -211,14 +210,14 @@ public class Flags {
public static final UnreleasedFlag REGION_SAMPLING = new UnreleasedFlag(801);
// 802 - wallpaper rendering
- public static final UnreleasedFlag USE_CANVAS_RENDERER = new UnreleasedFlag(802);
+ public static final UnreleasedFlag USE_CANVAS_RENDERER = new UnreleasedFlag(802, true);
// 803 - screen contents translation
public static final UnreleasedFlag SCREEN_CONTENTS_TRANSLATION = new UnreleasedFlag(803);
/***************************************/
// 900 - media
- public static final UnreleasedFlag MEDIA_TAP_TO_TRANSFER = new UnreleasedFlag(900);
+ public static final ReleasedFlag MEDIA_TAP_TO_TRANSFER = new ReleasedFlag(900);
public static final UnreleasedFlag MEDIA_SESSION_ACTIONS = new UnreleasedFlag(901);
public static final ReleasedFlag MEDIA_NEARBY_DEVICES = new ReleasedFlag(903);
public static final ReleasedFlag MEDIA_MUTE_AWAIT = new ReleasedFlag(904);
@@ -300,12 +299,16 @@ public class Flags {
public static final UnreleasedFlag SCREENSHOT_REQUEST_PROCESSOR = new UnreleasedFlag(1300);
public static final UnreleasedFlag SCREENSHOT_WORK_PROFILE_POLICY = new UnreleasedFlag(1301);
- // 1400 - columbus, b/242800729
- public static final UnreleasedFlag QUICK_TAP_IN_PCC = new UnreleasedFlag(1400);
+ // 1400 - columbus
+ public static final ReleasedFlag QUICK_TAP_IN_PCC = new ReleasedFlag(1400);
// 1500 - chooser
public static final UnreleasedFlag CHOOSER_UNBUNDLED = new UnreleasedFlag(1500);
+ // 1600 - accessibility
+ public static final UnreleasedFlag A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS =
+ new UnreleasedFlag(1600);
+
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
// | |
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt b/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt
new file mode 100644
index 000000000000..8f095a24de94
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 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.flags
+
+interface Restarter {
+ fun restart()
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index ad8c68807e01..c4eac1c3c401 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -662,7 +662,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
}
override fun onKeyguardDismissAmountChanged() {
- if (keyguardViewController.isShowing && !playingCannedUnlockAnimation) {
+ if (keyguardStateController.isShowing && !playingCannedUnlockAnimation) {
showOrHideSurfaceIfDismissAmountThresholdsReached()
// If the surface is visible or it's about to be, start updating its appearance to
@@ -721,7 +721,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
*/
private fun finishKeyguardExitRemoteAnimationIfReachThreshold() {
// no-op if keyguard is not showing or animation is not enabled.
- if (!keyguardViewController.isShowing) {
+ if (!keyguardStateController.isShowing) {
return
}
@@ -844,7 +844,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
* animation.
*/
fun hideKeyguardViewAfterRemoteAnimation() {
- if (keyguardViewController.isShowing) {
+ if (keyguardStateController.isShowing) {
// Hide the keyguard, with no fade out since we animated it away during the unlock.
keyguardViewController.hide(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7155acf78f58..aee70eeca12b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1875,7 +1875,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// if the keyguard is already showing, don't bother. check flags in both files
// to account for the hiding animation which results in a delay and discrepancy
// between flags
- if (mShowing && mKeyguardViewControllerLazy.get().isShowing()) {
+ if (mShowing && mKeyguardStateController.isShowing()) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
resetStateLocked();
return;
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 0c5564b34b15..28aa19e18e80 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -322,7 +322,7 @@ public class LogModule {
@SysUISingleton
@KeyguardUpdateMonitorLog
public static LogBuffer provideKeyguardUpdateMonitorLogBuffer(LogBufferFactory factory) {
- return factory.create("KeyguardUpdateMonitorLog", 200);
+ return factory.create("KeyguardUpdateMonitorLog", 400);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 2cd564ff4e32..9dd18b21cc71 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -886,7 +886,8 @@ class MediaCarouselController @Inject constructor(
interactedSubcardRank,
interactedSubcardCardinality,
receivedLatencyMillis,
- null // Media cards cannot have subcards.
+ null, // Media cards cannot have subcards.
+ null // Media cards don't have dimensions today.
)
/* ktlint-disable max-line-length */
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 00b0ff9b128d..f5caefbf4ced 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -22,6 +22,7 @@ import android.content.Context
import android.media.MediaRoute2Info
import android.util.Log
import androidx.annotation.VisibleForTesting
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
@@ -39,14 +40,10 @@ import javax.inject.Inject
*/
@SysUISingleton
class MediaTttCommandLineHelper @Inject constructor(
- commandRegistry: CommandRegistry,
+ private val commandRegistry: CommandRegistry,
private val context: Context,
@Main private val mainExecutor: Executor
-) {
- init {
- commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() }
- commandRegistry.registerCommand(RECEIVER_COMMAND) { ReceiverCommand() }
- }
+) : CoreStartable(context) {
/** All commands for the sender device. */
inner class SenderCommand : Command {
@@ -56,7 +53,7 @@ class MediaTttCommandLineHelper @Inject constructor(
val displayState: Int?
try {
displayState = ChipStateSender.getSenderStateIdFromName(commandName)
- } catch (ex: IllegalArgumentException) {
+ } catch (ex: IllegalArgumentException) {
pw.println("Invalid command name $commandName")
return
}
@@ -150,6 +147,11 @@ class MediaTttCommandLineHelper @Inject constructor(
"<chipState> useAppIcon=[true|false]")
}
}
+
+ override fun start() {
+ commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() }
+ commandRegistry.registerCommand(RECEIVER_COMMAND) { ReceiverCommand() }
+ }
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 8fc5519cc73e..1461293c7a07 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -19,6 +19,7 @@ package com.android.systemui.media.taptotransfer.receiver
import android.annotation.SuppressLint
import android.app.StatusBarManager
import android.content.Context
+import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.media.MediaRoute2Info
@@ -44,6 +45,7 @@ import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.animation.AnimationUtil.Companion.frames
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject
/**
@@ -53,7 +55,7 @@ import javax.inject.Inject
*/
@SysUISingleton
class MediaTttChipControllerReceiver @Inject constructor(
- commandQueue: CommandQueue,
+ private val commandQueue: CommandQueue,
context: Context,
@MediaTttReceiverLogger logger: MediaTttLogger,
windowManager: WindowManager,
@@ -63,6 +65,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
powerManager: PowerManager,
@Main private val mainHandler: Handler,
private val uiEventLogger: MediaTttReceiverUiEventLogger,
+ private val viewUtil: ViewUtil,
) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttLogger>(
context,
logger,
@@ -83,7 +86,6 @@ class MediaTttChipControllerReceiver @Inject constructor(
height = WindowManager.LayoutParams.MATCH_PARENT
layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
fitInsetsTypes = 0 // Ignore insets from all system bars
- flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
}
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
@@ -99,10 +101,6 @@ class MediaTttChipControllerReceiver @Inject constructor(
}
}
- init {
- commandQueue.addCallback(commandQueueCallbacks)
- }
-
private fun updateMediaTapToTransferReceiverDisplay(
@StatusBarManager.MediaTransferReceiverState displayState: Int,
routeInfo: MediaRoute2Info,
@@ -139,9 +137,11 @@ class MediaTttChipControllerReceiver @Inject constructor(
)
}
- override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
- super.updateView(newInfo, currentView)
+ override fun start() {
+ commandQueue.addCallback(commandQueueCallbacks)
+ }
+ override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(
context, newInfo.routeInfo.clientPackageName, logger
)
@@ -154,14 +154,14 @@ class MediaTttChipControllerReceiver @Inject constructor(
context.resources.getDimensionPixelSize(R.dimen.media_ttt_generic_icon_padding)
}
- val iconView = currentView.requireViewById<CachingIconView>(R.id.app_icon)
+ val iconView = currentView.getAppIconView()
iconView.setPadding(iconPadding, iconPadding, iconPadding, iconPadding)
iconView.setImageDrawable(iconDrawable)
iconView.contentDescription = iconContentDescription
}
override fun animateViewIn(view: ViewGroup) {
- val appIconView = view.requireViewById<View>(R.id.app_icon)
+ val appIconView = view.getAppIconView()
appIconView.animate()
.translationYBy(-1 * getTranslationAmount().toFloat())
.setDuration(30.frames)
@@ -175,6 +175,12 @@ class MediaTttChipControllerReceiver @Inject constructor(
startRipple(view.requireViewById(R.id.ripple))
}
+ override fun getTouchableRegion(view: View, outRect: Rect) {
+ // Even though the app icon view isn't touchable, users might think it is. So, use it as the
+ // touchable region to ensure that touches don't get passed to the window below.
+ viewUtil.setRectToViewWindowLocation(view.getAppIconView(), outRect)
+ }
+
/** Returns the amount that the chip will be translated by in its intro animation. */
private fun getTranslationAmount(): Int {
return context.resources.getDimensionPixelSize(R.dimen.media_ttt_receiver_vert_translation)
@@ -202,16 +208,19 @@ class MediaTttChipControllerReceiver @Inject constructor(
private fun layoutRipple(rippleView: ReceiverChipRippleView) {
val windowBounds = windowManager.currentWindowMetrics.bounds
- val height = windowBounds.height()
- val width = windowBounds.width()
+ val height = windowBounds.height().toFloat()
+ val width = windowBounds.width().toFloat()
- val maxDiameter = height / 2.5f
- rippleView.setMaxSize(maxDiameter, maxDiameter)
+ rippleView.setMaxSize(width / 2f, height / 2f)
// Center the ripple on the bottom of the screen in the middle.
- rippleView.setCenter(width * 0.5f, height.toFloat())
+ rippleView.setCenter(width * 0.5f, height)
val color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
rippleView.setColor(color, 70)
}
+
+ private fun View.getAppIconView(): CachingIconView {
+ return this.requireViewById(R.id.app_icon)
+ }
}
data class ChipReceiverInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index 6a505f06a495..e354a03f1725 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -18,6 +18,7 @@ package com.android.systemui.media.taptotransfer.receiver
import android.content.Context
import android.util.AttributeSet
+import com.android.systemui.ripple.RippleShader
import com.android.systemui.ripple.RippleView
/**
@@ -25,9 +26,9 @@ import com.android.systemui.ripple.RippleView
*/
class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleView(context, attrs) {
init {
- // TODO: use RippleShape#ELLIPSE when calling setupShader.
- setupShader()
+ setupShader(RippleShader.RippleShape.ELLIPSE)
setRippleFill(true)
+ setSparkleStrength(0f)
duration = 3000L
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 11c55285c00b..5d631450cb41 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -18,6 +18,7 @@ package com.android.systemui.media.taptotransfer.sender
import android.app.StatusBarManager
import android.content.Context
+import android.graphics.Rect
import android.media.MediaRoute2Info
import android.os.PowerManager
import android.util.Log
@@ -46,7 +47,7 @@ import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.concurrency.DelayableExecutor
-import dagger.Lazy
+import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject
/**
@@ -55,7 +56,7 @@ import javax.inject.Inject
*/
@SysUISingleton
open class MediaTttChipControllerSender @Inject constructor(
- commandQueue: CommandQueue,
+ private val commandQueue: CommandQueue,
context: Context,
@MediaTttSenderLogger logger: MediaTttLogger,
windowManager: WindowManager,
@@ -64,10 +65,9 @@ open class MediaTttChipControllerSender @Inject constructor(
configurationController: ConfigurationController,
powerManager: PowerManager,
private val uiEventLogger: MediaTttSenderUiEventLogger,
- // Added Lazy<> to delay the time we create Falsing instances.
- // And overcome performance issue, check [b/247817628] for details.
- private val falsingManager: Lazy<FalsingManager>,
- private val falsingCollector: Lazy<FalsingCollector>,
+ private val falsingManager: FalsingManager,
+ private val falsingCollector: FalsingCollector,
+ private val viewUtil: ViewUtil,
) : TemporaryViewDisplayController<ChipSenderInfo, MediaTttLogger>(
context,
logger,
@@ -99,10 +99,6 @@ open class MediaTttChipControllerSender @Inject constructor(
}
}
- init {
- commandQueue.addCallback(commandQueueCallbacks)
- }
-
private fun updateMediaTapToTransferSenderDisplay(
@StatusBarManager.MediaTransferSenderState displayState: Int,
routeInfo: MediaRoute2Info,
@@ -125,19 +121,21 @@ open class MediaTttChipControllerSender @Inject constructor(
}
}
+ override fun start() {
+ commandQueue.addCallback(commandQueueCallbacks)
+ }
+
override fun updateView(
newInfo: ChipSenderInfo,
currentView: ViewGroup
) {
- super.updateView(newInfo, currentView)
-
val chipState = newInfo.state
// Detect falsing touches on the chip.
parent = currentView.requireViewById(R.id.media_ttt_sender_chip)
parent.touchHandler = object : Gefingerpoken {
override fun onTouchEvent(ev: MotionEvent?): Boolean {
- falsingCollector.get().onTouchEvent(ev)
+ falsingCollector.onTouchEvent(ev)
return false
}
}
@@ -166,7 +164,7 @@ open class MediaTttChipControllerSender @Inject constructor(
newInfo.routeInfo,
newInfo.undoCallback,
uiEventLogger,
- falsingManager.get(),
+ falsingManager,
)
undoView.setOnClickListener(undoClickListener)
undoView.visibility = (undoClickListener != null).visibleIfTrue()
@@ -207,10 +205,10 @@ open class MediaTttChipControllerSender @Inject constructor(
// animateChipOut matches the animateChipIn.
}
- override fun shouldIgnoreViewRemoval(removalReason: String): Boolean {
+ override fun shouldIgnoreViewRemoval(info: ChipSenderInfo, removalReason: String): Boolean {
// Don't remove the chip if we're in progress or succeeded, since the user should still be
// able to see the status of the transfer. (But do remove it if it's finally timed out.)
- val transferStatus = info?.state?.transferStatus
+ val transferStatus = info.state.transferStatus
if (
(transferStatus == TransferStatus.IN_PROGRESS ||
transferStatus == TransferStatus.SUCCEEDED) &&
@@ -224,6 +222,10 @@ open class MediaTttChipControllerSender @Inject constructor(
return false
}
+ override fun getTouchableRegion(view: View, outRect: Rect) {
+ viewUtil.setRectToViewWindowLocation(view, outRect)
+ }
+
private fun Boolean.visibleIfTrue(): Int {
return if (this) {
View.VISIBLE
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index da9fefab0b66..33021e3cde47 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -48,7 +48,6 @@ import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
-import com.android.keyguard.KeyguardViewController;
import com.android.systemui.Dumpable;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -61,6 +60,7 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -90,7 +90,7 @@ public final class NavBarHelper implements
private final AccessibilityManager mAccessibilityManager;
private final Lazy<AssistManager> mAssistManagerLazy;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
- private final KeyguardViewController mKeyguardViewController;
+ private final KeyguardStateController mKeyguardStateController;
private final UserTracker mUserTracker;
private final SystemActions mSystemActions;
private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
@@ -125,7 +125,7 @@ public final class NavBarHelper implements
OverviewProxyService overviewProxyService,
Lazy<AssistManager> assistManagerLazy,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
- KeyguardViewController keyguardViewController,
+ KeyguardStateController keyguardStateController,
NavigationModeController navigationModeController,
UserTracker userTracker,
DumpManager dumpManager) {
@@ -134,7 +134,7 @@ public final class NavBarHelper implements
mAccessibilityManager = accessibilityManager;
mAssistManagerLazy = assistManagerLazy;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
- mKeyguardViewController = keyguardViewController;
+ mKeyguardStateController = keyguardStateController;
mUserTracker = userTracker;
mSystemActions = systemActions;
accessibilityManager.addAccessibilityServicesStateChangeListener(this);
@@ -326,7 +326,7 @@ public final class NavBarHelper implements
shadeWindowView =
mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
}
- boolean isKeyguardShowing = mKeyguardViewController.isShowing();
+ boolean isKeyguardShowing = mKeyguardStateController.isShowing();
boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
&& shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
return imeVisibleOnShade
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index 6793f0163c43..43cf6231aaf0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -36,6 +36,7 @@ import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -75,15 +76,27 @@ public final class NavigationBarTransitions extends BarTransitions implements
private List<DarkIntensityListener> mDarkIntensityListeners;
private final Handler mHandler = Handler.getMain();
- private final IWallpaperVisibilityListener mWallpaperVisibilityListener =
- new IWallpaperVisibilityListener.Stub() {
+
+ static final class WallpaperVisibilityListener extends IWallpaperVisibilityListener.Stub {
+ private final WeakReference<NavigationBarTransitions> mSelf;
+
+ WallpaperVisibilityListener(NavigationBarTransitions self) {
+ mSelf = new WeakReference<>(self);
+ }
+
@Override
public void onWallpaperVisibilityChanged(boolean newVisibility,
- int displayId) throws RemoteException {
- mWallpaperVisible = newVisibility;
- mHandler.post(() -> applyLightsOut(true, false));
+ int displayId) throws RemoteException {
+ NavigationBarTransitions self = mSelf.get();
+ if (self == null) {
+ return;
+ }
+ self.mWallpaperVisible = newVisibility;
+ self.mHandler.post(() -> self.applyLightsOut(true, false));
}
- };
+ }
+
+ private final IWallpaperVisibilityListener mWallpaperVisibilityListener;
@Inject
public NavigationBarTransitions(
@@ -91,6 +104,7 @@ public final class NavigationBarTransitions extends BarTransitions implements
IWindowManager windowManagerService,
LightBarTransitionsController.Factory lightBarTransitionsControllerFactory) {
super(view, R.drawable.nav_background);
+
mView = view;
mWindowManagerService = windowManagerService;
mLightTransitionsController = lightBarTransitionsControllerFactory.create(this);
@@ -98,6 +112,7 @@ public final class NavigationBarTransitions extends BarTransitions implements
.getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
mDarkIntensityListeners = new ArrayList();
+ mWallpaperVisibilityListener = new WallpaperVisibilityListener(this);
try {
mWallpaperVisible = mWindowManagerService.registerWallpaperVisibilityListener(
mWallpaperVisibilityListener, Display.DEFAULT_DISPLAY);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index 28ddead0bdd9..dd1ffcc9fa12 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -222,6 +222,7 @@ object FooterActionsViewBinder {
private fun bindButton(button: IconButtonViewHolder, model: FooterActionsButtonViewModel?) {
val buttonView = button.view
+ buttonView.id = model?.id ?: View.NO_ID
buttonView.isVisible = model != null
if (model == null) {
return
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
index 2ad0513c2ace..9b5f683d8dab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
@@ -25,6 +25,7 @@ import com.android.systemui.common.shared.model.Icon
* power buttons.
*/
data class FooterActionsButtonViewModel(
+ val id: Int,
val icon: Icon,
val iconTint: Int?,
@DrawableRes val background: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index a935338c2565..11d955580983 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -138,6 +138,7 @@ class FooterActionsViewModel(
/** The model for the settings button. */
val settings: FooterActionsButtonViewModel =
FooterActionsButtonViewModel(
+ id = R.id.settings_button_container,
Icon.Resource(
R.drawable.ic_settings,
ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
@@ -151,6 +152,7 @@ class FooterActionsViewModel(
val power: FooterActionsButtonViewModel? =
if (showPowerButton) {
FooterActionsButtonViewModel(
+ id = R.id.pm_lite,
Icon.Resource(
android.R.drawable.ic_lock_power_off,
ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
@@ -256,6 +258,7 @@ class FooterActionsViewModel(
}
return FooterActionsButtonViewModel(
+ id = R.id.multi_user_switch,
Icon.Loaded(
icon,
ContentDescription.Loaded(userSwitcherContentDescription(status.currentUserName)),
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index df32d2081fde..704e11512b37 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -408,24 +408,6 @@ public class ScreenshotController {
}
/**
- * Displays a screenshot selector
- */
- @MainThread
- void takeScreenshotPartial(ComponentName topComponent,
- final Consumer<Uri> finisher, RequestCallback requestCallback) {
- Assert.isMainThread();
- mScreenshotView.reset();
- mCurrentRequestCallback = requestCallback;
-
- attachWindow();
- mWindow.setContentView(mScreenshotView);
- mScreenshotView.requestApplyInsets();
-
- mScreenshotView.takePartialScreenshot(
- rect -> takeScreenshotInternal(topComponent, finisher, rect));
- }
-
- /**
* Clears current screenshot
*/
void dismissScreenshot(boolean immediate) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java
deleted file mode 100644
index c793b5b9639e..000000000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.screenshot;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-
-import java.util.function.Consumer;
-
-/**
- * Draws a selection rectangle while taking screenshot
- */
-public class ScreenshotSelectorView extends View {
- private Point mStartPoint;
- private Rect mSelectionRect;
- private final Paint mPaintSelection, mPaintBackground;
-
- private Consumer<Rect> mOnScreenshotSelected;
-
- public ScreenshotSelectorView(Context context) {
- this(context, null);
- }
-
- public ScreenshotSelectorView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- mPaintBackground = new Paint(Color.BLACK);
- mPaintBackground.setAlpha(160);
- mPaintSelection = new Paint(Color.TRANSPARENT);
- mPaintSelection.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
-
- setOnTouchListener((v, event) -> {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- startSelection((int) event.getX(), (int) event.getY());
- return true;
- case MotionEvent.ACTION_MOVE:
- updateSelection((int) event.getX(), (int) event.getY());
- return true;
- case MotionEvent.ACTION_UP:
- setVisibility(View.GONE);
- final Rect rect = getSelectionRect();
- if (mOnScreenshotSelected != null
- && rect != null
- && rect.width() != 0 && rect.height() != 0) {
- mOnScreenshotSelected.accept(rect);
- }
- stopSelection();
- return true;
- }
- return false;
- });
- }
-
- @Override
- public void draw(Canvas canvas) {
- canvas.drawRect(mLeft, mTop, mRight, mBottom, mPaintBackground);
- if (mSelectionRect != null) {
- canvas.drawRect(mSelectionRect, mPaintSelection);
- }
- }
-
- void setOnScreenshotSelected(Consumer<Rect> onScreenshotSelected) {
- mOnScreenshotSelected = onScreenshotSelected;
- }
-
- void stop() {
- if (getSelectionRect() != null) {
- stopSelection();
- }
- }
-
- private void startSelection(int x, int y) {
- mStartPoint = new Point(x, y);
- mSelectionRect = new Rect(x, y, x, y);
- }
-
- private void updateSelection(int x, int y) {
- if (mSelectionRect != null) {
- mSelectionRect.left = Math.min(mStartPoint.x, x);
- mSelectionRect.right = Math.max(mStartPoint.x, x);
- mSelectionRect.top = Math.min(mStartPoint.y, y);
- mSelectionRect.bottom = Math.max(mStartPoint.y, y);
- invalidate();
- }
- }
-
- private Rect getSelectionRect() {
- return mSelectionRect;
- }
-
- private void stopSelection() {
- mStartPoint = null;
- mSelectionRect = null;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 360fc879731c..be41a6b0d376 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -93,7 +93,6 @@ import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.QuickStepContract;
import java.util.ArrayList;
-import java.util.function.Consumer;
/**
* Handles the visual elements and animations for the screenshot flow.
@@ -141,7 +140,6 @@ public class ScreenshotView extends FrameLayout implements
private boolean mOrientationPortrait;
private boolean mDirectionLTR;
- private ScreenshotSelectorView mScreenshotSelectorView;
private ImageView mScrollingScrim;
private DraggableConstraintLayout mScreenshotStatic;
private ImageView mScreenshotPreview;
@@ -361,7 +359,6 @@ public class ScreenshotView extends FrameLayout implements
mDismissButton = requireNonNull(findViewById(R.id.screenshot_dismiss_button));
mScrollablePreview = requireNonNull(findViewById(R.id.screenshot_scrollable_preview));
mScreenshotFlash = requireNonNull(findViewById(R.id.screenshot_flash));
- mScreenshotSelectorView = requireNonNull(findViewById(R.id.screenshot_selector));
mShareChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_share_chip));
mEditChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_edit_chip));
mScrollChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_scroll_chip));
@@ -377,8 +374,6 @@ public class ScreenshotView extends FrameLayout implements
mActionsContainerBackground.setTouchDelegate(actionsDelegate);
setFocusable(true);
- mScreenshotSelectorView.setFocusable(true);
- mScreenshotSelectorView.setFocusableInTouchMode(true);
mActionsContainer.setScrollX(0);
mNavMode = getResources().getInteger(
@@ -432,12 +427,6 @@ public class ScreenshotView extends FrameLayout implements
mCallbacks = callbacks;
}
- void takePartialScreenshot(Consumer<Rect> onPartialScreenshotSelected) {
- mScreenshotSelectorView.setOnScreenshotSelected(onPartialScreenshotSelected);
- mScreenshotSelectorView.setVisibility(View.VISIBLE);
- mScreenshotSelectorView.requestFocus();
- }
-
void setScreenshot(Bitmap bitmap, Insets screenInsets) {
mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets));
}
@@ -1031,7 +1020,6 @@ public class ScreenshotView extends FrameLayout implements
mQuickShareChip = null;
setAlpha(1);
mScreenshotStatic.setAlpha(1);
- mScreenshotSelectorView.stop();
}
private void startSharedTransition(ActionTransition transition) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 695a80b2b95d..a4a59ce52c7a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -249,12 +249,6 @@ public class TakeScreenshotService extends Service {
}
mScreenshot.takeScreenshotFullscreen(topComponent, uriConsumer, callback);
break;
- case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
- if (DEBUG_SERVICE) {
- Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_SELECTED_REGION");
- }
- mScreenshot.takeScreenshotPartial(topComponent, uriConsumer, callback);
- break;
case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
if (DEBUG_SERVICE) {
Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_PROVIDED_IMAGE");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index c337dead9fdd..9f15627514d5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -4287,7 +4287,7 @@ public final class NotificationPanelViewController extends PanelViewController {
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyExpanded()
- && mStatusBarKeyguardViewManager.isShowing()) {
+ && mKeyguardStateController.isShowing()) {
mStatusBarKeyguardViewManager.updateKeyguardPosition(event.getX());
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/OWNERS b/packages/SystemUI/src/com/android/systemui/shade/OWNERS
index 7dc9dc7efeb7..d71fbf656e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/shade/OWNERS
@@ -1,3 +1,6 @@
+justinweir@google.com
+syeonlee@google.com
+
per-file *Notification* = set noparent
per-file *Notification* = file:../statusbar/notification/OWNERS
@@ -11,4 +14,4 @@ per-file NotificationShadeWindowView.java = pixel@google.com, cinek@google.com,
per-file NotificationPanelUnfoldAnimationController.kt = alexflo@google.com, jeffdq@google.com, juliacr@google.com
per-file NotificationPanelView.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com
-per-file NotificationPanelViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com \ No newline at end of file
+per-file NotificationPanelViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
index 1be4c04ef804..b5c7ef5f7630 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification;
-import android.annotation.Nullable;
import android.util.ArraySet;
import androidx.annotation.VisibleForTesting;
@@ -25,7 +24,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
@@ -43,7 +41,6 @@ public class DynamicPrivacyController implements KeyguardStateController.Callbac
private boolean mLastDynamicUnlocked;
private boolean mCacheInvalid;
- @Nullable private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Inject
DynamicPrivacyController(NotificationLockscreenUserManager notificationLockscreenUserManager,
@@ -100,7 +97,7 @@ public class DynamicPrivacyController implements KeyguardStateController.Callbac
* contents aren't revealed yet?
*/
public boolean isInLockedDownShade() {
- if (!isStatusBarKeyguardShowing() || !mKeyguardStateController.isMethodSecure()) {
+ if (!mKeyguardStateController.isShowing() || !mKeyguardStateController.isMethodSecure()) {
return false;
}
int state = mStateController.getState();
@@ -113,16 +110,7 @@ public class DynamicPrivacyController implements KeyguardStateController.Callbac
return true;
}
- private boolean isStatusBarKeyguardShowing() {
- return mStatusBarKeyguardViewManager != null && mStatusBarKeyguardViewManager.isShowing();
- }
-
- public void setStatusBarKeyguardViewManager(
- StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
- mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
- }
-
public interface Listener {
void onDynamicPrivacyChanged();
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index a7719d3d82a4..e71d80c130da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.collection.inflation;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+
import android.os.SystemClock;
import android.service.notification.NotificationStats;
@@ -70,6 +72,8 @@ public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback
dismissalSurface = NotificationStats.DISMISSAL_PEEK;
} else if (mStatusBarStateController.isDozing()) {
dismissalSurface = NotificationStats.DISMISSAL_AOD;
+ } else if (mStatusBarStateController.getState() == KEYGUARD) {
+ dismissalSurface = NotificationStats.DISMISSAL_LOCKSCREEN;
}
return new DismissedByUserStats(
dismissalSurface,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index 3292a8fcdb50..6cf4bf318c99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -37,6 +37,19 @@ public interface NotificationInterruptStateProvider {
boolean shouldHeadsUp(NotificationEntry entry);
/**
+ * Returns the value of whether this entry should peek (from shouldHeadsUp(entry)), but only
+ * optionally logs the status.
+ *
+ * This method should be used in cases where the caller needs to check whether a notification
+ * qualifies for a heads up, but is not necessarily guaranteed to make the heads-up happen.
+ *
+ * @param entry the entry to check
+ * @param log whether or not to log the results of this check
+ * @return true if the entry should heads up, false otherwise
+ */
+ boolean checkHeadsUp(NotificationEntry entry, boolean log);
+
+ /**
* Whether the notification should appear as a bubble with a fly-out on top of the screen.
*
* @param entry the entry to check
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 558fd62c78bf..c5a69217a1ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -137,11 +137,11 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
public boolean shouldBubbleUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
- if (!canAlertCommon(entry)) {
+ if (!canAlertCommon(entry, true)) {
return false;
}
- if (!canAlertAwakeCommon(entry)) {
+ if (!canAlertAwakeCommon(entry, true)) {
return false;
}
@@ -163,10 +163,15 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
@Override
public boolean shouldHeadsUp(NotificationEntry entry) {
+ return checkHeadsUp(entry, true);
+ }
+
+ @Override
+ public boolean checkHeadsUp(NotificationEntry entry, boolean log) {
if (mStatusBarStateController.isDozing()) {
- return shouldHeadsUpWhenDozing(entry);
+ return shouldHeadsUpWhenDozing(entry, log);
} else {
- return shouldHeadsUpWhenAwake(entry);
+ return shouldHeadsUpWhenAwake(entry, log);
}
}
@@ -263,61 +268,61 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
}
}
- private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) {
+ private boolean shouldHeadsUpWhenAwake(NotificationEntry entry, boolean log) {
StatusBarNotification sbn = entry.getSbn();
if (!mUseHeadsUp) {
- mLogger.logNoHeadsUpFeatureDisabled();
+ if (log) mLogger.logNoHeadsUpFeatureDisabled();
return false;
}
- if (!canAlertCommon(entry)) {
+ if (!canAlertCommon(entry, log)) {
return false;
}
- if (!canAlertHeadsUpCommon(entry)) {
+ if (!canAlertHeadsUpCommon(entry, log)) {
return false;
}
- if (!canAlertAwakeCommon(entry)) {
+ if (!canAlertAwakeCommon(entry, log)) {
return false;
}
if (isSnoozedPackage(sbn)) {
- mLogger.logNoHeadsUpPackageSnoozed(entry);
+ if (log) mLogger.logNoHeadsUpPackageSnoozed(entry);
return false;
}
boolean inShade = mStatusBarStateController.getState() == SHADE;
if (entry.isBubble() && inShade) {
- mLogger.logNoHeadsUpAlreadyBubbled(entry);
+ if (log) mLogger.logNoHeadsUpAlreadyBubbled(entry);
return false;
}
if (entry.shouldSuppressPeek()) {
- mLogger.logNoHeadsUpSuppressedByDnd(entry);
+ if (log) mLogger.logNoHeadsUpSuppressedByDnd(entry);
return false;
}
if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
- mLogger.logNoHeadsUpNotImportant(entry);
+ if (log) mLogger.logNoHeadsUpNotImportant(entry);
return false;
}
boolean inUse = mPowerManager.isScreenOn() && !isDreaming();
if (!inUse) {
- mLogger.logNoHeadsUpNotInUse(entry);
+ if (log) mLogger.logNoHeadsUpNotInUse(entry);
return false;
}
for (int i = 0; i < mSuppressors.size(); i++) {
if (mSuppressors.get(i).suppressAwakeHeadsUp(entry)) {
- mLogger.logNoHeadsUpSuppressedBy(entry, mSuppressors.get(i));
+ if (log) mLogger.logNoHeadsUpSuppressedBy(entry, mSuppressors.get(i));
return false;
}
}
- mLogger.logHeadsUp(entry);
+ if (log) mLogger.logHeadsUp(entry);
return true;
}
@@ -328,37 +333,37 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
* @param entry the entry to check
* @return true if the entry should ambient pulse, false otherwise
*/
- private boolean shouldHeadsUpWhenDozing(NotificationEntry entry) {
+ private boolean shouldHeadsUpWhenDozing(NotificationEntry entry, boolean log) {
if (!mAmbientDisplayConfiguration.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) {
- mLogger.logNoPulsingSettingDisabled(entry);
+ if (log) mLogger.logNoPulsingSettingDisabled(entry);
return false;
}
if (mBatteryController.isAodPowerSave()) {
- mLogger.logNoPulsingBatteryDisabled(entry);
+ if (log) mLogger.logNoPulsingBatteryDisabled(entry);
return false;
}
- if (!canAlertCommon(entry)) {
- mLogger.logNoPulsingNoAlert(entry);
+ if (!canAlertCommon(entry, log)) {
+ if (log) mLogger.logNoPulsingNoAlert(entry);
return false;
}
- if (!canAlertHeadsUpCommon(entry)) {
- mLogger.logNoPulsingNoAlert(entry);
+ if (!canAlertHeadsUpCommon(entry, log)) {
+ if (log) mLogger.logNoPulsingNoAlert(entry);
return false;
}
if (entry.shouldSuppressAmbient()) {
- mLogger.logNoPulsingNoAmbientEffect(entry);
+ if (log) mLogger.logNoPulsingNoAmbientEffect(entry);
return false;
}
if (entry.getImportance() < NotificationManager.IMPORTANCE_DEFAULT) {
- mLogger.logNoPulsingNotImportant(entry);
+ if (log) mLogger.logNoPulsingNotImportant(entry);
return false;
}
- mLogger.logPulsing(entry);
+ if (log) mLogger.logPulsing(entry);
return true;
}
@@ -366,18 +371,22 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
* Common checks between regular & AOD heads up and bubbles.
*
* @param entry the entry to check
+ * @param log whether or not to log the results of these checks
* @return true if these checks pass, false if the notification should not alert
*/
- private boolean canAlertCommon(NotificationEntry entry) {
+ private boolean canAlertCommon(NotificationEntry entry, boolean log) {
for (int i = 0; i < mSuppressors.size(); i++) {
if (mSuppressors.get(i).suppressInterruptions(entry)) {
- mLogger.logNoAlertingSuppressedBy(entry, mSuppressors.get(i), /* awake */ false);
+ if (log) {
+ mLogger.logNoAlertingSuppressedBy(entry, mSuppressors.get(i),
+ /* awake */ false);
+ }
return false;
}
}
if (mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry)) {
- mLogger.keyguardHideNotification(entry);
+ if (log) mLogger.keyguardHideNotification(entry);
return false;
}
@@ -388,19 +397,20 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
* Common checks for heads up notifications on regular and AOD displays.
*
* @param entry the entry to check
+ * @param log whether or not to log the results of these checks
* @return true if these checks pass, false if the notification should not alert
*/
- private boolean canAlertHeadsUpCommon(NotificationEntry entry) {
+ private boolean canAlertHeadsUpCommon(NotificationEntry entry, boolean log) {
StatusBarNotification sbn = entry.getSbn();
// Don't alert notifications that are suppressed due to group alert behavior
if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
- mLogger.logNoAlertingGroupAlertBehavior(entry);
+ if (log) mLogger.logNoAlertingGroupAlertBehavior(entry);
return false;
}
if (entry.hasJustLaunchedFullScreenIntent()) {
- mLogger.logNoAlertingRecentFullscreen(entry);
+ if (log) mLogger.logNoAlertingRecentFullscreen(entry);
return false;
}
@@ -413,12 +423,14 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
* @param entry the entry to check
* @return true if these checks pass, false if the notification should not alert
*/
- private boolean canAlertAwakeCommon(NotificationEntry entry) {
+ private boolean canAlertAwakeCommon(NotificationEntry entry, boolean log) {
StatusBarNotification sbn = entry.getSbn();
for (int i = 0; i < mSuppressors.size(); i++) {
if (mSuppressors.get(i).suppressAwakeInterruptions(entry)) {
- mLogger.logNoAlertingSuppressedBy(entry, mSuppressors.get(i), /* awake */ true);
+ if (log) {
+ mLogger.logNoAlertingSuppressedBy(entry, mSuppressors.get(i), /* awake */ true);
+ }
return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ce465bcfb42d..2719dd88b7be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -44,7 +44,8 @@ import java.io.PrintWriter;
import javax.inject.Inject;
/**
- * A global state to track all input states for the algorithm.
+ * Global state to track all input states for
+ * {@link com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm}.
*/
@SysUISingleton
public class AmbientState implements Dumpable {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 4576a6442838..9900e41214d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -150,7 +150,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private final KeyguardBypassController mKeyguardBypassController;
private PowerManager.WakeLock mWakeLock;
private final KeyguardUpdateMonitor mUpdateMonitor;
- private final DozeParameters mDozeParameters;
private final KeyguardStateController mKeyguardStateController;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final SessionTracker mSessionTracker;
@@ -262,7 +261,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
KeyguardStateController keyguardStateController, Handler handler,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@Main Resources resources,
- KeyguardBypassController keyguardBypassController, DozeParameters dozeParameters,
+ KeyguardBypassController keyguardBypassController,
MetricsLogger metricsLogger, DumpManager dumpManager,
PowerManager powerManager,
NotificationMediaManager notificationMediaManager,
@@ -276,7 +275,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
VibratorHelper vibrator) {
mPowerManager = powerManager;
mUpdateMonitor = keyguardUpdateMonitor;
- mDozeParameters = dozeParameters;
mUpdateMonitor.registerCallback(this);
mMediaManager = notificationMediaManager;
mLatencyTracker = latencyTracker;
@@ -522,7 +520,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
boolean deviceDreaming = mUpdateMonitor.isDreaming();
if (!mUpdateMonitor.isDeviceInteractive()) {
- if (!mKeyguardViewController.isShowing()
+ if (!mKeyguardStateController.isShowing()
&& !mScreenOffAnimationController.isKeyguardShowDelayed()) {
if (mKeyguardStateController.isUnlocked()) {
return MODE_WAKE_AND_UNLOCK;
@@ -539,7 +537,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
if (unlockingAllowed && deviceDreaming) {
return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
}
- if (mKeyguardViewController.isShowing()) {
+ if (mKeyguardStateController.isShowing()) {
if (mKeyguardViewController.bouncerIsOrWillBeShowing() && unlockingAllowed) {
return MODE_DISMISS_BOUNCER;
} else if (unlockingAllowed) {
@@ -558,7 +556,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
boolean bypass = mKeyguardBypassController.getBypassEnabled()
|| mAuthController.isUdfpsFingerDown();
if (!mUpdateMonitor.isDeviceInteractive()) {
- if (!mKeyguardViewController.isShowing()) {
+ if (!mKeyguardStateController.isShowing()) {
return bypass ? MODE_WAKE_AND_UNLOCK : MODE_ONLY_WAKE;
} else if (!unlockingAllowed) {
return bypass ? MODE_SHOW_BOUNCER : MODE_NONE;
@@ -582,7 +580,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
if (unlockingAllowed && mKeyguardStateController.isOccluded()) {
return MODE_UNLOCK_COLLAPSING;
}
- if (mKeyguardViewController.isShowing()) {
+ if (mKeyguardStateController.isShowing()) {
if ((mKeyguardViewController.bouncerIsOrWillBeShowing()
|| mKeyguardBypassController.getAltBouncerShowing()) && unlockingAllowed) {
return MODE_DISMISS_BOUNCER;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 961b43347a9e..25fd483efa2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -416,6 +416,9 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void endAffordanceLaunch();
+ /** Should the keyguard be hidden immediately in response to a back press/gesture. */
+ boolean shouldKeyguardHideImmediately();
+
boolean onBackPressed();
boolean onSpacePressed();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index e30bc686c8b0..d6fadcab9c25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -363,7 +363,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
mKeyguardUpdateMonitor.onCameraLaunched();
}
- if (!mStatusBarKeyguardViewManager.isShowing()) {
+ if (!mKeyguardStateController.isShowing()) {
final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
mCentralSurfaces.startActivityDismissingKeyguard(cameraIntent,
false /* onlyProvisioned */, true /* dismissShade */,
@@ -420,7 +420,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
// TODO(b/169087248) Possibly add haptics here for emergency action. Currently disabled for
// app-side haptic experimentation.
- if (!mStatusBarKeyguardViewManager.isShowing()) {
+ if (!mKeyguardStateController.isShowing()) {
mCentralSurfaces.startActivityDismissingKeyguard(emergencyIntent,
false /* onlyProvisioned */, true /* dismissShade */,
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 62614d0d7867..b6e658fd4eab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -459,7 +459,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
private final KeyguardStateController mKeyguardStateController;
private final HeadsUpManagerPhone mHeadsUpManager;
private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
- private final DynamicPrivacyController mDynamicPrivacyController;
private final FalsingCollector mFalsingCollector;
private final FalsingManager mFalsingManager;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -762,7 +761,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
mHeadsUpManager = headsUpManagerPhone;
mKeyguardIndicationController = keyguardIndicationController;
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
- mDynamicPrivacyController = dynamicPrivacyController;
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
@@ -1553,7 +1551,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
mRemoteInputManager.addControllerCallback(mStatusBarKeyguardViewManager);
- mDynamicPrivacyController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
mMediaManager.setBiometricUnlockController(mBiometricUnlockController);
@@ -2062,7 +2059,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
// Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
// the bouncer appear animation.
- if (!mStatusBarKeyguardViewManager.isShowing()) {
+ if (!mKeyguardStateController.isShowing()) {
WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
}
}
@@ -2499,8 +2496,8 @@ public class CentralSurfacesImpl extends CoreStartable implements
};
// Do not deferKeyguard when occluded because, when keyguard is occluded,
// we do not launch the activity until keyguard is done.
- boolean occluded = mStatusBarKeyguardViewManager.isShowing()
- && mStatusBarKeyguardViewManager.isOccluded();
+ boolean occluded = mKeyguardStateController.isShowing()
+ && mKeyguardStateController.isOccluded();
boolean deferred = !occluded;
executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly,
willLaunchResolverActivity, deferred /* deferred */, animate);
@@ -2570,8 +2567,8 @@ public class CentralSurfacesImpl extends CoreStartable implements
@Override
public boolean onDismiss() {
if (runnable != null) {
- if (mStatusBarKeyguardViewManager.isShowing()
- && mStatusBarKeyguardViewManager.isOccluded()) {
+ if (mKeyguardStateController.isShowing()
+ && mKeyguardStateController.isOccluded()) {
mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
} else {
mMainExecutor.execute(runnable);
@@ -2665,7 +2662,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
private void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen,
boolean afterKeyguardGone) {
- if (mStatusBarKeyguardViewManager.isShowing() && requiresShadeOpen) {
+ if (mKeyguardStateController.isShowing() && requiresShadeOpen) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
}
dismissKeyguardThenExecute(action, null /* cancelAction */,
@@ -2689,7 +2686,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
mBiometricUnlockController.startWakeAndUnlock(
BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
}
- if (mStatusBarKeyguardViewManager.isShowing()) {
+ if (mKeyguardStateController.isShowing()) {
mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
afterKeyguardGone);
} else {
@@ -2825,8 +2822,8 @@ public class CentralSurfacesImpl extends CoreStartable implements
}
private void logStateToEventlog() {
- boolean isShowing = mStatusBarKeyguardViewManager.isShowing();
- boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded();
+ boolean isShowing = mKeyguardStateController.isShowing();
+ boolean isOccluded = mKeyguardStateController.isOccluded();
boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
boolean isSecure = mKeyguardStateController.isMethodSecure();
boolean unlocked = mKeyguardStateController.canDismissLockScreen();
@@ -3222,18 +3219,17 @@ public class CentralSurfacesImpl extends CoreStartable implements
Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0);
Trace.beginSection("CentralSurfaces#updateDozingState");
- boolean visibleNotOccluded = mStatusBarKeyguardViewManager.isShowing()
- && !mStatusBarKeyguardViewManager.isOccluded();
+ boolean keyguardVisible = mKeyguardStateController.isVisible();
// If we're dozing and we'll be animating the screen off, the keyguard isn't currently
// visible but will be shortly for the animation, so we should proceed as if it's visible.
- boolean visibleNotOccludedOrWillBe =
- visibleNotOccluded || (mDozing && mDozeParameters.shouldDelayKeyguardShow());
+ boolean keyguardVisibleOrWillBe =
+ keyguardVisible || (mDozing && mDozeParameters.shouldDelayKeyguardShow());
boolean wakeAndUnlock = mBiometricUnlockController.getMode()
== BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup() && !wakeAndUnlock)
|| (mDozing && mDozeParameters.shouldControlScreenOff()
- && visibleNotOccludedOrWillBe);
+ && keyguardVisibleOrWillBe);
mNotificationPanelViewController.setDozing(mDozing, animate);
updateQsExpansionEnabled();
@@ -3292,19 +3288,23 @@ public class CentralSurfacesImpl extends CoreStartable implements
mNotificationPanelViewController.onAffordanceLaunchEnded();
}
+ /**
+ * Returns whether the keyguard should hide immediately (as opposed to via an animation).
+ * Non-scrimmed bouncers have a special animation tied to the notification panel expansion.
+ * @return whether the keyguard should be immediately hidden.
+ */
@Override
- public boolean onBackPressed() {
+ public boolean shouldKeyguardHideImmediately() {
final boolean isScrimmedBouncer =
mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
final boolean isBouncerOverDream = isBouncerShowingOverDream();
+ return (isScrimmedBouncer || isBouncerOverDream);
+ }
- if (mStatusBarKeyguardViewManager.onBackPressed(
- isScrimmedBouncer || isBouncerOverDream /* hideImmediately */)) {
- if (isScrimmedBouncer || isBouncerOverDream) {
- mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
- } else {
- mNotificationPanelViewController.expandWithoutQs();
- }
+ @Override
+ public boolean onBackPressed() {
+ if (mStatusBarKeyguardViewManager.canHandleBackPressed()) {
+ mStatusBarKeyguardViewManager.onBackPressed();
return true;
}
if (mNotificationPanelViewController.isQsCustomizing()) {
@@ -3319,7 +3319,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
return true;
}
if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED
- && !isBouncerOverDream) {
+ && !isBouncerShowingOverDream()) {
if (mNotificationPanelViewController.canPanelBeCollapsed()) {
mShadeController.animateCollapsePanels();
}
@@ -3911,11 +3911,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
@Override
public boolean isKeyguardShowing() {
- if (mStatusBarKeyguardViewManager == null) {
- Slog.i(TAG, "isKeyguardShowing() called before startKeyguard(), returning true");
- return true;
- }
- return mStatusBarKeyguardViewManager.isShowing();
+ return mKeyguardStateController.isShowing();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index d4fa1f27c8f6..5f5ec68ba898 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -31,12 +31,15 @@ import android.hardware.biometrics.BiometricSourceType;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.Trace;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.WindowManagerGlobal;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -119,6 +122,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private static final long KEYGUARD_DISMISS_DURATION_LOCKED = 2000;
private static String TAG = "StatusBarKeyguardViewManager";
+ private static final boolean DEBUG = false;
protected final Context mContext;
private final ConfigurationController mConfigurationController;
@@ -184,8 +188,25 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mAlternateAuthInterceptor != null) {
mAlternateAuthInterceptor.onBouncerVisibilityChanged();
}
+
+ /* Register predictive back callback when keyguard becomes visible, and unregister
+ when it's hidden. */
+ if (isVisible) {
+ registerBackCallback();
+ } else {
+ unregisterBackCallback();
+ }
}
};
+
+ private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
+ if (DEBUG) {
+ Log.d(TAG, "onBackInvokedCallback() called, invoking onBackPressed()");
+ }
+ onBackPressed();
+ };
+ private boolean mIsBackCallbackRegistered = false;
+
private final DockManager.DockEventListener mDockEventListener =
new DockManager.DockEventListener() {
@Override
@@ -209,8 +230,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private View mNotificationContainer;
@Nullable protected KeyguardBouncer mBouncer;
- protected boolean mShowing;
- protected boolean mOccluded;
protected boolean mRemoteInputActive;
private boolean mGlobalActionsVisible = false;
private boolean mLastGlobalActionsVisible = false;
@@ -257,10 +276,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
new KeyguardUpdateMonitorCallback() {
@Override
public void onEmergencyCallAction() {
-
// Since we won't get a setOccluded call we have to reset the view manually such that
// the bouncer goes away.
- if (mOccluded) {
+ if (mKeyguardStateController.isOccluded()) {
reset(true /* hideBouncerWhenShowing */);
}
}
@@ -380,6 +398,46 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
}
+ /** Register a callback, to be invoked by the Predictive Back system. */
+ private void registerBackCallback() {
+ if (!mIsBackCallbackRegistered) {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_OVERLAY, mOnBackInvokedCallback);
+ mIsBackCallbackRegistered = true;
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "view root was null, could not register back callback");
+ }
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "prevented registering back callback twice");
+ }
+ }
+ }
+
+ /** Unregister the callback formerly registered with the Predictive Back system. */
+ private void unregisterBackCallback() {
+ if (mIsBackCallbackRegistered) {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(
+ mOnBackInvokedCallback);
+ mIsBackCallbackRegistered = false;
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "view root was null, could not unregister back callback");
+ }
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "prevented unregistering back callback twice");
+ }
+ }
+ }
+
@Override
public void onDensityOrFontScaleChanged() {
hideBouncer(true /* destroyView */);
@@ -420,7 +478,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
} else {
mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
}
- } else if (mShowing && !hideBouncerOverDream) {
+ } else if (mKeyguardStateController.isShowing() && !hideBouncerOverDream) {
if (!isWakeAndUnlocking()
&& !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
&& !mCentralSurfaces.isInLaunchTransition()
@@ -441,7 +499,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBouncerInteractor.show(/* isScrimmed= */false);
}
}
- } else if (!mShowing && isBouncerInTransit()) {
+ } else if (!mKeyguardStateController.isShowing() && isBouncerInTransit()) {
// Keyguard is not visible anymore, but expansion animation was still running.
// We need to hide the bouncer, otherwise it will be stuck in transit.
if (mBouncer != null) {
@@ -474,9 +532,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void show(Bundle options) {
Trace.beginSection("StatusBarKeyguardViewManager#show");
- mShowing = true;
mNotificationShadeWindowController.setKeyguardShowing(true);
- mKeyguardStateController.notifyKeyguardState(mShowing,
+ mKeyguardStateController.notifyKeyguardState(true,
mKeyguardStateController.isOccluded());
reset(true /* hideBouncerWhenShowing */);
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
@@ -540,7 +597,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
} else {
mBouncerInteractor.hide();
}
- if (mShowing) {
+ if (mKeyguardStateController.isShowing()) {
// If we were showing the bouncer and then aborting, we need to also clear out any
// potential actions unless we actually unlocked.
cancelPostAuthActions();
@@ -557,7 +614,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void showBouncer(boolean scrimmed) {
resetAlternateAuth(false);
- if (mShowing && !isBouncerShowing()) {
+ if (mKeyguardStateController.isShowing() && !isBouncerShowing()) {
if (mBouncer != null) {
mBouncer.show(false /* resetSecuritySelection */, scrimmed);
} else {
@@ -574,7 +631,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void dismissWithAction(OnDismissAction r, Runnable cancelAction,
boolean afterKeyguardGone, String message) {
- if (mShowing) {
+ if (mKeyguardStateController.isShowing()) {
try {
Trace.beginSection("StatusBarKeyguardViewManager#dismissWithAction");
cancelPendingWakeupAction();
@@ -660,11 +717,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void reset(boolean hideBouncerWhenShowing) {
- if (mShowing) {
+ if (mKeyguardStateController.isShowing()) {
+ final boolean isOccluded = mKeyguardStateController.isOccluded();
// Hide quick settings.
- mNotificationPanelViewController.resetViews(/* animate= */ !mOccluded);
+ mNotificationPanelViewController.resetViews(/* animate= */ !isOccluded);
// Hide bouncer and quick-quick settings.
- if (mOccluded && !mDozing) {
+ if (isOccluded && !mDozing) {
mCentralSurfaces.hideKeyguard();
if (hideBouncerWhenShowing || needsFullscreenBouncer()) {
hideBouncer(false /* destroyView */);
@@ -746,7 +804,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private void setDozing(boolean dozing) {
if (mDozing != dozing) {
mDozing = dozing;
- if (dozing || mBouncer.needsFullscreenBouncer() || mOccluded) {
+ if (dozing || mBouncer.needsFullscreenBouncer()
+ || mKeyguardStateController.isOccluded()) {
reset(dozing /* hideBouncerWhenShowing */);
}
updateStates();
@@ -779,18 +838,23 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void setOccluded(boolean occluded, boolean animate) {
- final boolean isOccluding = !mOccluded && occluded;
- final boolean isUnOccluding = mOccluded && !occluded;
- setOccludedAndUpdateStates(occluded);
+ final boolean wasOccluded = mKeyguardStateController.isOccluded();
+ final boolean isOccluding = !wasOccluded && occluded;
+ final boolean isUnOccluding = wasOccluded && !occluded;
+ mKeyguardStateController.notifyKeyguardState(
+ mKeyguardStateController.isShowing(), occluded);
+ updateStates();
+ final boolean isShowing = mKeyguardStateController.isShowing();
+ final boolean isOccluded = mKeyguardStateController.isOccluded();
- if (mShowing && isOccluding) {
+ if (isShowing && isOccluding) {
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED);
if (mCentralSurfaces.isInLaunchTransition()) {
final Runnable endRunnable = new Runnable() {
@Override
public void run() {
- mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
+ mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
reset(true /* hideBouncerWhenShowing */);
}
};
@@ -805,19 +869,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
// When isLaunchingActivityOverLockscreen() is true, we know for sure that the post
// collapse runnables will be run.
mShadeController.get().addPostCollapseAction(() -> {
- mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
+ mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
reset(true /* hideBouncerWhenShowing */);
});
return;
}
- } else if (mShowing && isUnOccluding) {
+ } else if (isShowing && isUnOccluding) {
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
}
- if (mShowing) {
- mMediaManager.updateMediaMetaData(false, animate && !mOccluded);
+ if (isShowing) {
+ mMediaManager.updateMediaMetaData(false, animate && !isOccluded);
}
- mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
+ mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
// setDozing(false) will call reset once we stop dozing. Also, if we're going away, there's
// no need to reset the keyguard views as we'll be gone shortly. Resetting now could cause
@@ -827,20 +891,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
// by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
reset(isOccluding /* hideBouncerWhenShowing*/);
}
- if (animate && !mOccluded && mShowing && !bouncerIsShowing()) {
+ if (animate && !isOccluded && isShowing && !bouncerIsShowing()) {
mCentralSurfaces.animateKeyguardUnoccluding();
}
}
- private void setOccludedAndUpdateStates(boolean occluded) {
- mOccluded = occluded;
- updateStates();
- }
-
- public boolean isOccluded() {
- return mOccluded;
- }
-
@Override
public void startPreHideAnimation(Runnable finishRunnable) {
if (bouncerIsShowing()) {
@@ -871,8 +926,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void hide(long startTime, long fadeoutDuration) {
Trace.beginSection("StatusBarKeyguardViewManager#hide");
- mShowing = false;
- mKeyguardStateController.notifyKeyguardState(mShowing,
+ mKeyguardStateController.notifyKeyguardState(false,
mKeyguardStateController.isOccluded());
launchPendingWakeupAction();
@@ -1020,33 +1074,43 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
KeyguardUpdateMonitor.getCurrentUser()) != KeyguardSecurityModel.SecurityMode.None;
}
- @Override
- public boolean isShowing() {
- return mShowing;
+ /**
+ * Returns whether a back invocation can be handled, which depends on whether the keyguard
+ * is currently showing (which itself is derived from multiple states).
+ *
+ * @return whether a back press can be handled right now.
+ */
+ public boolean canHandleBackPressed() {
+ return mBouncer.isShowing();
}
/**
* Notifies this manager that the back button has been pressed.
- *
- * @param hideImmediately Hide bouncer when {@code true}, keep it around otherwise.
- * Non-scrimmed bouncers have a special animation tied to the expansion
- * of the notification panel.
- * @return whether the back press has been handled
*/
- public boolean onBackPressed(boolean hideImmediately) {
- if (bouncerIsShowing()) {
- mCentralSurfaces.endAffordanceLaunch();
- // The second condition is for SIM card locked bouncer
- if (bouncerIsScrimmed()
- && !needsFullscreenBouncer()) {
- hideBouncer(false);
- updateStates();
+ public void onBackPressed() {
+ if (!canHandleBackPressed()) {
+ return;
+ }
+
+ mCentralSurfaces.endAffordanceLaunch();
+ // The second condition is for SIM card locked bouncer
+ if (bouncerIsScrimmed() && needsFullscreenBouncer()) {
+ hideBouncer(false);
+ updateStates();
+ } else {
+ /* Non-scrimmed bouncers have a special animation tied to the expansion
+ * of the notification panel. We decide whether to kick this animation off
+ * by computing the hideImmediately boolean.
+ */
+ boolean hideImmediately = mCentralSurfaces.shouldKeyguardHideImmediately();
+ reset(hideImmediately);
+ if (hideImmediately) {
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
} else {
- reset(hideImmediately);
+ mNotificationPanelViewController.expandWithoutQs();
}
- return true;
}
- return false;
+ return;
}
@Override
@@ -1108,8 +1172,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (!mCentralSurfacesRegistered) {
return;
}
- boolean showing = mShowing;
- boolean occluded = mOccluded;
+ boolean showing = mKeyguardStateController.isShowing();
+ boolean occluded = mKeyguardStateController.isOccluded();
boolean bouncerShowing = bouncerIsShowing();
boolean bouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing();
boolean bouncerDismissible = !isFullscreenBouncer();
@@ -1143,13 +1207,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mNotificationShadeWindowController.setBouncerShowing(bouncerShowing);
mCentralSurfaces.setBouncerShowing(bouncerShowing);
}
-
- if (occluded != mLastOccluded || mFirstUpdate) {
- mKeyguardStateController.notifyKeyguardState(showing, occluded);
- }
- if (occluded != mLastOccluded || mShowing != showing || mFirstUpdate) {
- mKeyguardUpdateManager.setKeyguardShowing(showing, occluded);
- }
if (bouncerIsOrWillBeShowing != mLastBouncerIsOrWillBeShowing || mFirstUpdate
|| bouncerShowing != mLastBouncerShowing) {
mKeyguardUpdateManager.sendKeyguardBouncerChanged(bouncerIsOrWillBeShowing,
@@ -1200,12 +1257,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public boolean isNavBarVisible() {
boolean isWakeAndUnlockPulsing = mBiometricUnlockController != null
&& mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
- boolean keyguardShowing = mShowing && !mOccluded;
+ boolean keyguardVisible = mKeyguardStateController.isVisible();
boolean hideWhileDozing = mDozing && !isWakeAndUnlockPulsing;
- boolean keyguardWithGestureNav = (keyguardShowing && !mDozing && !mScreenOffAnimationPlaying
+ boolean keyguardWithGestureNav = (keyguardVisible && !mDozing && !mScreenOffAnimationPlaying
|| mPulsing && !mIsDocked)
&& mGesturalNav;
- return (!keyguardShowing && !hideWhileDozing && !mScreenOffAnimationPlaying
+ return (!keyguardVisible && !hideWhileDozing && !mScreenOffAnimationPlaying
|| bouncerIsShowing()
|| mRemoteInputActive
|| keyguardWithGestureNav
@@ -1322,14 +1379,22 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public ViewRootImpl getViewRootImpl() {
- return mNotificationShadeWindowController.getNotificationShadeView().getViewRootImpl();
+ ViewGroup viewGroup = mNotificationShadeWindowController.getNotificationShadeView();
+ if (viewGroup != null) {
+ return viewGroup.getViewRootImpl();
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "ViewGroup was null, cannot get ViewRootImpl");
+ }
+ return null;
+ }
}
public void launchPendingWakeupAction() {
DismissWithActionRequest request = mPendingWakeupAction;
mPendingWakeupAction = null;
if (request != null) {
- if (mShowing) {
+ if (mKeyguardStateController.isShowing()) {
dismissWithAction(request.dismissAction, request.cancelAction,
request.afterKeyguardGone, request.message);
} else if (request.dismissAction != null) {
@@ -1348,10 +1413,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public boolean bouncerNeedsScrimming() {
// When a dream overlay is active, scrimming will cause any expansion to immediately expand.
- return (mOccluded && !mDreamOverlayStateController.isOverlayActive())
+ return (mKeyguardStateController.isOccluded()
+ && !mDreamOverlayStateController.isOverlayActive())
|| bouncerWillDismissWithAction()
- || (bouncerIsShowing()
- && bouncerIsScrimmed())
+ || (bouncerIsShowing() && bouncerIsScrimmed())
|| isFullscreenBouncer();
}
@@ -1370,8 +1435,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void dump(PrintWriter pw) {
pw.println("StatusBarKeyguardViewManager:");
- pw.println(" mShowing: " + mShowing);
- pw.println(" mOccluded: " + mOccluded);
pw.println(" mRemoteInputActive: " + mRemoteInputActive);
pw.println(" mDozing: " + mDozing);
pw.println(" mAfterKeyguardGoneAction: " + mAfterKeyguardGoneAction);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AospPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AospPolicyModule.java
new file mode 100644
index 000000000000..ba5fa1df4b15
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AospPolicyModule.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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.policy;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.PowerManager;
+
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.power.EnhancedEstimates;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * com.android.systemui.statusbar.policy related providers that others may want to override.
+ */
+@Module
+public class AospPolicyModule {
+ @Provides
+ @SysUISingleton
+ static BatteryController provideBatteryController(
+ Context context,
+ EnhancedEstimates enhancedEstimates,
+ PowerManager powerManager,
+ BroadcastDispatcher broadcastDispatcher,
+ DemoModeController demoModeController,
+ DumpManager dumpManager,
+ @Main Handler mainHandler,
+ @Background Handler bgHandler) {
+ BatteryController bC = new BatteryControllerImpl(
+ context,
+ enhancedEstimates,
+ powerManager,
+ broadcastDispatcher,
+ demoModeController,
+ dumpManager,
+ mainHandler,
+ bgHandler);
+ bC.init();
+ return bC;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index 250d9d46de66..1ae1eae00651 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -35,9 +35,16 @@ public interface KeyguardStateController extends CallbackController<Callback> {
}
/**
- * If the lock screen is visible.
- * The keyguard is also visible when the device is asleep or in always on mode, except when
- * the screen timed out and the user can unlock by quickly pressing power.
+ * If the keyguard is visible. This is unrelated to being locked or not.
+ */
+ default boolean isVisible() {
+ return isShowing() && !isOccluded();
+ }
+
+ /**
+ * If the keyguard is showing. This includes when it's occluded by an activity, and when
+ * the device is asleep or in always on mode, except when the screen timed out and the user
+ * can unlock by quickly pressing power.
*
* This is unrelated to being locked or not.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 437d4d415275..cc6fdccba789 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -181,6 +181,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
if (mShowing == showing && mOccluded == occluded) return;
mShowing = showing;
mOccluded = occluded;
+ mKeyguardUpdateMonitor.setKeyguardShowing(showing, occluded);
Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events",
"Keyguard showing: " + showing + " occluded: " + occluded);
notifyKeyguardChanged();
@@ -387,6 +388,8 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardStateController:");
+ pw.println(" mShowing: " + mShowing);
+ pw.println(" mOccluded: " + mOccluded);
pw.println(" mSecure: " + mSecure);
pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
pw.println(" mTrustManaged: " + mTrustManaged);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt
index 16926566105c..af39eeed26b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt
@@ -30,7 +30,6 @@ import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.user.data.source.UserRecord
import com.android.systemui.user.domain.interactor.GuestUserInteractor
import com.android.systemui.user.domain.interactor.UserInteractor
-import com.android.systemui.user.legacyhelper.data.LegacyUserDataHelper
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
import dagger.Lazy
import java.io.PrintWriter
@@ -118,7 +117,7 @@ constructor(
dialogShower: UserSwitchDialogController.DialogShower?
) {
if (useInteractor) {
- userInteractor.selectUser(userId)
+ userInteractor.selectUser(userId, dialogShower)
} else {
_oldImpl.onUserSelected(userId, dialogShower)
}
@@ -203,11 +202,7 @@ constructor(
dialogShower: UserSwitchDialogController.DialogShower?,
) {
if (useInteractor) {
- if (LegacyUserDataHelper.isUser(record)) {
- userInteractor.selectUser(record.resolveId())
- } else {
- userInteractor.executeAction(LegacyUserDataHelper.toUserActionModel(record))
- }
+ userInteractor.onRecordSelected(record, dialogShower)
} else {
_oldImpl.onUserListItemClicked(record, dialogShower)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index e0d780a5fcd5..afcf7a9ec66d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.window;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
+import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
@@ -27,6 +30,7 @@ import static com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
@@ -36,6 +40,7 @@ import android.util.Log;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.IWindowManager;
+import android.view.InsetsFrameProvider;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
@@ -221,6 +226,19 @@ public class StatusBarWindowController {
lp.setTitle("StatusBar");
lp.packageName = mContext.getPackageName();
lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ final InsetsFrameProvider gestureInsetsProvider =
+ new InsetsFrameProvider(ITYPE_TOP_MANDATORY_GESTURES);
+ final int safeTouchRegionHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.display_cutout_touchable_region_size);
+ if (safeTouchRegionHeight > 0) {
+ gestureInsetsProvider.minimalInsetsSizeInDisplayCutoutSafe =
+ Insets.of(0, safeTouchRegionHeight, 0, 0);
+ }
+ lp.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(ITYPE_STATUS_BAR),
+ new InsetsFrameProvider(ITYPE_TOP_TAPPABLE_ELEMENT),
+ gestureInsetsProvider
+ };
return lp;
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 91e20ee30976..4450b76a878c 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -20,17 +20,19 @@ import android.annotation.LayoutRes
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.PixelFormat
+import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.os.PowerManager
import android.os.SystemClock
import android.view.LayoutInflater
+import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
-import androidx.annotation.CallSuper
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -60,7 +62,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
@LayoutRes private val viewLayoutRes: Int,
private val windowTitle: String,
private val wakeReason: String,
-) {
+) : CoreStartable(context) {
/**
* Window layout params that will be used as a starting point for the [windowLayoutParams] of
* all subclasses.
@@ -70,7 +72,8 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
width = WindowManager.LayoutParams.WRAP_CONTENT
height = WindowManager.LayoutParams.WRAP_CONTENT
type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY
- flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
title = windowTitle
format = PixelFormat.TRANSLUCENT
setTrustedOverlay()
@@ -84,11 +87,8 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
*/
internal abstract val windowLayoutParams: WindowManager.LayoutParams
- /** The view currently being displayed. Null if the view is not being displayed. */
- private var view: ViewGroup? = null
-
- /** The info currently being displayed. Null if the view is not being displayed. */
- internal var info: T? = null
+ /** A container for all the display-related objects. Null if the view is not being displayed. */
+ private var displayInfo: DisplayInfo? = null
/** A [Runnable] that, when run, will cancel the pending timeout of the view. */
private var cancelViewTimeout: Runnable? = null
@@ -100,10 +100,11 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
* display the correct information in the view.
*/
fun displayView(newInfo: T) {
- val currentView = view
+ val currentDisplayInfo = displayInfo
- if (currentView != null) {
- updateView(newInfo, currentView)
+ if (currentDisplayInfo != null) {
+ currentDisplayInfo.info = newInfo
+ updateView(currentDisplayInfo.info, currentDisplayInfo.view)
} else {
// The view is new, so set up all our callbacks and inflate the view
configurationController.addCallback(displayScaleListener)
@@ -140,19 +141,24 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
val newView = LayoutInflater
.from(context)
.inflate(viewLayoutRes, null) as ViewGroup
- view = newView
- updateView(newInfo, newView)
+ val newViewController = TouchableRegionViewController(newView, this::getTouchableRegion)
+ newViewController.init()
+
+ // We don't need to hold on to the view controller since we never set anything additional
+ // on it -- it will be automatically cleaned up when the view is detached.
+ val newDisplayInfo = DisplayInfo(newView, newInfo)
+ displayInfo = newDisplayInfo
+ updateView(newDisplayInfo.info, newDisplayInfo.view)
windowManager.addView(newView, windowLayoutParams)
animateViewIn(newView)
}
/** Removes then re-inflates the view. */
private fun reinflateView() {
- val currentInfo = info
- if (view == null || currentInfo == null) { return }
+ val currentViewInfo = displayInfo ?: return
- windowManager.removeView(view)
- inflateAndUpdateView(currentInfo)
+ windowManager.removeView(currentViewInfo.view)
+ inflateAndUpdateView(currentViewInfo.info)
}
private val displayScaleListener = object : ConfigurationController.ConfigurationListener {
@@ -168,20 +174,20 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
* change, etc.)
*/
fun removeView(removalReason: String) {
- if (shouldIgnoreViewRemoval(removalReason)) {
+ val currentDisplayInfo = displayInfo ?: return
+ if (shouldIgnoreViewRemoval(currentDisplayInfo.info, removalReason)) {
return
}
- val currentView = view ?: return
+ val currentView = currentDisplayInfo.view
animateViewOut(currentView) { windowManager.removeView(currentView) }
logger.logChipRemoval(removalReason)
configurationController.removeCallback(displayScaleListener)
- // Re-set the view to null immediately (instead as part of the animation end runnable) so
+ // Re-set to null immediately (instead as part of the animation end runnable) so
// that if a new view event comes in while this view is animating out, we still display the
// new view appropriately.
- view = null
- info = null
+ displayInfo = null
// No need to time the view out since it's already gone
cancelViewTimeout?.run()
}
@@ -191,15 +197,18 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
*
* Allows subclasses to keep the view visible for longer in certain circumstances.
*/
- open fun shouldIgnoreViewRemoval(removalReason: String): Boolean = false
+ open fun shouldIgnoreViewRemoval(info: T, removalReason: String): Boolean = false
/**
* A method implemented by subclasses to update [currentView] based on [newInfo].
*/
- @CallSuper
- open fun updateView(newInfo: T, currentView: ViewGroup) {
- info = newInfo
- }
+ abstract fun updateView(newInfo: T, currentView: ViewGroup)
+
+ /**
+ * Fills [outRect] with the touchable region of this view. This will be used by WindowManager
+ * to decide which touch events go to the view.
+ */
+ abstract fun getTouchableRegion(view: View, outRect: Rect)
/**
* A method that can be implemented by subclasses to do custom animations for when the view
@@ -216,6 +225,15 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
internal open fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
onAnimationEnd.run()
}
+
+ /** A container for all the display-related state objects. */
+ private inner class DisplayInfo(
+ /** The view currently being displayed. */
+ val view: ViewGroup,
+
+ /** The info currently being displayed. */
+ var info: T,
+ )
}
object TemporaryDisplayRemovalReason {
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TouchableRegionViewController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TouchableRegionViewController.kt
new file mode 100644
index 000000000000..60241a9684d9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TouchableRegionViewController.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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.temporarydisplay
+
+import android.graphics.Rect
+import android.view.View
+import android.view.ViewTreeObserver
+import com.android.systemui.util.ViewController
+
+/**
+ * A view controller that will notify the [ViewTreeObserver] about the touchable region for this
+ * view. This will be used by WindowManager to decide which touch events go to the view and which
+ * pass through to the window below.
+ *
+ * @param touchableRegionSetter a function that, given the view and an out rect, fills the rect with
+ * the touchable region of this view.
+ */
+class TouchableRegionViewController(
+ view: View,
+ touchableRegionSetter: (View, Rect) -> Unit,
+) : ViewController<View>(view) {
+
+ private val tempRect = Rect()
+
+ private val internalInsetsListener =
+ ViewTreeObserver.OnComputeInternalInsetsListener { inoutInfo ->
+ inoutInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION
+ )
+
+ tempRect.setEmpty()
+ touchableRegionSetter.invoke(mView, tempRect)
+ inoutInfo.touchableRegion.set(tempRect)
+ }
+
+ public override fun onViewAttached() {
+ mView.viewTreeObserver.addOnComputeInternalInsetsListener(internalInsetsListener)
+ }
+
+ public override fun onViewDetached() {
+ mView.viewTreeObserver.removeOnComputeInternalInsetsListener(internalInsetsListener)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index adef1823d491..a345d99b47c6 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -171,6 +171,10 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
@Override
public void onColorsChanged(WallpaperColors wallpaperColors, int which, int userId) {
+ WallpaperColors currentColors = mCurrentColors.get(userId);
+ if (wallpaperColors != null && wallpaperColors.equals(currentColors)) {
+ return;
+ }
boolean currentUser = userId == mUserTracker.getUserId();
if (currentUser && !mAcceptColorEvents
&& mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP) {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 00ed3d635fa1..3ce5ca3d0301 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -22,25 +22,20 @@ import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
import android.content.Context;
import android.hardware.SensorPrivacyManager;
import android.os.Handler;
-import android.os.PowerManager;
import androidx.annotation.Nullable;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardViewController;
-import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.ReferenceSystemUIModule;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.gestural.GestureModule;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.dagger.PowerModule;
import com.android.systemui.privacy.MediaProjectionPrivacyItemMonitor;
import com.android.systemui.privacy.PrivacyItemMonitor;
@@ -64,8 +59,7 @@ import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryControllerImpl;
+import com.android.systemui.statusbar.policy.AospPolicyModule;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
@@ -86,18 +80,21 @@ import dagger.Provides;
import dagger.multibindings.IntoSet;
/**
- * A dagger module for injecting default implementations of components of System UI that may be
- * overridden by the System UI implementation.
+ * A TV specific version of {@link ReferenceSystemUIModule}.
+ *
+ * Code here should be specific to the TV variant of SystemUI and will not be included in other
+ * variants of SystemUI.
*/
-@Module(includes = {
- GestureModule.class,
- PowerModule.class,
- QSModule.class,
- ReferenceScreenshotModule.class,
- VolumeModule.class,
- },
- subcomponents = {
- })
+@Module(
+ includes = {
+ AospPolicyModule.class,
+ GestureModule.class,
+ PowerModule.class,
+ QSModule.class,
+ ReferenceScreenshotModule.class,
+ VolumeModule.class,
+ }
+)
public abstract class TvSystemUIModule {
@SysUISingleton
@@ -114,21 +111,6 @@ public abstract class TvSystemUIModule {
@Provides
@SysUISingleton
- static BatteryController provideBatteryController(Context context,
- EnhancedEstimates enhancedEstimates, PowerManager powerManager,
- BroadcastDispatcher broadcastDispatcher, DemoModeController demoModeController,
- DumpManager dumpManager,
- @Main Handler mainHandler, @Background Handler bgHandler) {
- BatteryController bC = new BatteryControllerImpl(context, enhancedEstimates, powerManager,
- broadcastDispatcher, demoModeController,
- dumpManager,
- mainHandler, bgHandler);
- bC.init();
- return bC;
- }
-
- @Provides
- @SysUISingleton
static SensorPrivacyController provideSensorPrivacyController(
SensorPrivacyManager sensorPrivacyManager) {
SensorPrivacyController spC = new SensorPrivacyControllerImpl(sensorPrivacyManager);
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 3014f39c17f8..919e699652bc 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -220,7 +220,7 @@ constructor(
val result = withContext(backgroundDispatcher) { manager.aliveUsers }
if (result != null) {
- _userInfos.value = result
+ _userInfos.value = result.sortedBy { it.creationTime }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index a84238c1559a..142a328b2bc4 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -43,6 +43,7 @@ import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.statusbar.policy.UserSwitcherController
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.data.repository.UserRepository
@@ -390,9 +391,24 @@ constructor(
guestUserInteractor.onDeviceBootCompleted()
}
+ /** Switches to the user or executes the action represented by the given record. */
+ fun onRecordSelected(
+ record: UserRecord,
+ dialogShower: UserSwitchDialogController.DialogShower? = null,
+ ) {
+ if (LegacyUserDataHelper.isUser(record)) {
+ // It's safe to use checkNotNull around record.info because isUser only returns true
+ // if record.info is not null.
+ selectUser(checkNotNull(record.info).id, dialogShower)
+ } else {
+ executeAction(LegacyUserDataHelper.toUserActionModel(record), dialogShower)
+ }
+ }
+
/** Switches to the user with the given user ID. */
fun selectUser(
newlySelectedUserId: Int,
+ dialogShower: UserSwitchDialogController.DialogShower? = null,
) {
if (isNewImpl) {
val currentlySelectedUserInfo = repository.getSelectedUserInfo()
@@ -428,22 +444,28 @@ constructor(
return
}
+ dialogShower?.dismiss()
+
switchUser(newlySelectedUserId)
} else {
- controller.onUserSelected(newlySelectedUserId, /* dialogShower= */ null)
+ controller.onUserSelected(newlySelectedUserId, dialogShower)
}
}
/** Executes the given action. */
- fun executeAction(action: UserActionModel) {
+ fun executeAction(
+ action: UserActionModel,
+ dialogShower: UserSwitchDialogController.DialogShower? = null,
+ ) {
if (isNewImpl) {
when (action) {
UserActionModel.ENTER_GUEST_MODE ->
guestUserInteractor.createAndSwitchTo(
this::showDialog,
this::dismissDialog,
- this::selectUser,
- )
+ ) { userId ->
+ selectUser(userId, dialogShower)
+ }
UserActionModel.ADD_USER -> {
val currentUser = repository.getSelectedUserInfo()
showDialog(
@@ -575,7 +597,7 @@ constructor(
}
private fun switchUser(userId: Int) {
- // TODO(b/246631653): track jank and lantecy like in the old impl.
+ // TODO(b/246631653): track jank and latency like in the old impl.
refreshUsersScheduler.pause()
try {
activityManager.switchUser(userId)
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java b/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java
index db35437e77b9..ecb365f43e3f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java
@@ -34,9 +34,26 @@ public abstract class Condition implements CallbackController<Condition.Callback
private final String mTag = getClass().getSimpleName();
private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
- private boolean mIsConditionMet = false;
+ private final boolean mOverriding;
+ private Boolean mIsConditionMet;
private boolean mStarted = false;
- private boolean mOverriding = false;
+
+ /**
+ * By default, conditions have an initial value of false and are not overriding.
+ */
+ public Condition() {
+ this(false, false);
+ }
+
+ /**
+ * Constructor for specifying initial state and overriding condition attribute.
+ * @param initialConditionMet Initial state of the condition.
+ * @param overriding Whether this condition overrides others.
+ */
+ protected Condition(Boolean initialConditionMet, boolean overriding) {
+ mIsConditionMet = initialConditionMet;
+ mOverriding = overriding;
+ }
/**
* Starts monitoring the condition.
@@ -49,14 +66,6 @@ public abstract class Condition implements CallbackController<Condition.Callback
protected abstract void stop();
/**
- * Sets whether this condition's value overrides others in determining the overall state.
- */
- public void setOverriding(boolean overriding) {
- mOverriding = overriding;
- updateCondition(mIsConditionMet);
- }
-
- /**
* Returns whether the current condition overrides
*/
public boolean isOverridingCondition() {
@@ -110,13 +119,31 @@ public abstract class Condition implements CallbackController<Condition.Callback
* @param isConditionMet True if the condition has been fulfilled. False otherwise.
*/
protected void updateCondition(boolean isConditionMet) {
- if (mIsConditionMet == isConditionMet) {
+ if (mIsConditionMet != null && mIsConditionMet == isConditionMet) {
return;
}
if (shouldLog()) Log.d(mTag, "updating condition to " + isConditionMet);
mIsConditionMet = isConditionMet;
+ sendUpdate();
+ }
+ /**
+ * Clears the set condition value. This is purposefully separate from
+ * {@link #updateCondition(boolean)} to avoid confusion around {@code null} values.
+ */
+ protected void clearCondition() {
+ if (mIsConditionMet == null) {
+ return;
+ }
+
+ if (shouldLog()) Log.d(mTag, "clearing condition");
+
+ mIsConditionMet = null;
+ sendUpdate();
+ }
+
+ private void sendUpdate() {
final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
while (iterator.hasNext()) {
final Callback cb = iterator.next().get();
@@ -128,8 +155,21 @@ public abstract class Condition implements CallbackController<Condition.Callback
}
}
+ /**
+ * Returns whether the condition is set. This method should be consulted to understand the
+ * value of {@link #isConditionMet()}.
+ * @return {@code true} if value is present, {@code false} otherwise.
+ */
+ public boolean isConditionSet() {
+ return mIsConditionMet != null;
+ }
+
+ /**
+ * Returns whether the condition has been met. Note that this method will return {@code false}
+ * if the condition is not set as well.
+ */
public boolean isConditionMet() {
- return mIsConditionMet;
+ return Boolean.TRUE.equals(mIsConditionMet);
}
private boolean shouldLog() {
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
index dac8a0bff28a..4824f6744c6e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
@@ -57,12 +57,16 @@ public class Monitor {
}
public void update() {
+ // Only consider set conditions.
+ final Collection<Condition> setConditions = mSubscription.mConditions.stream()
+ .filter(Condition::isConditionSet).collect(Collectors.toSet());
+
// Overriding conditions do not override each other
- final Collection<Condition> overridingConditions = mSubscription.mConditions.stream()
+ final Collection<Condition> overridingConditions = setConditions.stream()
.filter(Condition::isOverridingCondition).collect(Collectors.toSet());
final Collection<Condition> targetCollection = overridingConditions.isEmpty()
- ? mSubscription.mConditions : overridingConditions;
+ ? setConditions : overridingConditions;
final boolean newAllConditionsMet = targetCollection.isEmpty() ? true : targetCollection
.stream()
diff --git a/packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt b/packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt
index 613a797f020c..6160b00379ef 100644
--- a/packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt
@@ -1,5 +1,22 @@
+/*
+ * Copyright (C) 2022 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.util.view
+import android.graphics.Rect
import android.view.View
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
@@ -23,4 +40,22 @@ class ViewUtil @Inject constructor() {
top <= y &&
y <= top + view.height
}
+
+ /**
+ * Sets [outRect] to be the view's location within its window.
+ */
+ fun setRectToViewWindowLocation(view: View, outRect: Rect) {
+ val locInWindow = IntArray(2)
+ view.getLocationInWindow(locInWindow)
+
+ val x = locInWindow[0]
+ val y = locInWindow[1]
+
+ outRect.set(
+ x,
+ y,
+ x + view.width,
+ y + view.height,
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index 80385e69cfa4..f5cd0ca7ab3b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -41,6 +41,7 @@ import android.testing.TestableLooper.RunWithLooper;
import android.testing.ViewUtils;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceView;
+import android.view.View;
import androidx.test.filters.SmallTest;
@@ -84,6 +85,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mKeyguardSecurityContainer = spy(new KeyguardSecurityContainer(mContext));
+ mKeyguardSecurityContainer.setId(View.generateViewId());
ViewUtils.attachView(mKeyguardSecurityContainer);
mTestableLooper = TestableLooper.get(this);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 25e7dbb5f26d..8a2c35410586 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -22,9 +22,11 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.plugins.Clock
import com.android.systemui.plugins.ClockAnimations
+import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockEvents
+import com.android.systemui.plugins.ClockFaceController
+import com.android.systemui.plugins.ClockFaceEvents
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -40,6 +42,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
@@ -61,17 +64,25 @@ class ClockEventControllerTest : SysuiTestCase() {
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var animations: ClockAnimations
@Mock private lateinit var events: ClockEvents
- @Mock private lateinit var clock: Clock
+ @Mock private lateinit var clock: ClockController
@Mock private lateinit var mainExecutor: Executor
@Mock private lateinit var bgExecutor: Executor
@Mock private lateinit var featureFlags: FeatureFlags
+ @Mock private lateinit var smallClockController: ClockFaceController
+ @Mock private lateinit var largeClockController: ClockFaceController
+ @Mock private lateinit var smallClockEvents: ClockFaceEvents
+ @Mock private lateinit var largeClockEvents: ClockFaceEvents
private lateinit var clockEventController: ClockEventController
@Before
fun setUp() {
- whenever(clock.smallClock).thenReturn(TextView(context))
- whenever(clock.largeClock).thenReturn(TextView(context))
+ whenever(clock.smallClock).thenReturn(smallClockController)
+ whenever(clock.largeClock).thenReturn(largeClockController)
+ whenever(smallClockController.view).thenReturn(TextView(context))
+ whenever(largeClockController.view).thenReturn(TextView(context))
+ whenever(smallClockController.events).thenReturn(smallClockEvents)
+ whenever(largeClockController.events).thenReturn(largeClockEvents)
whenever(clock.events).thenReturn(events)
whenever(clock.animations).thenReturn(animations)
@@ -107,7 +118,8 @@ class ClockEventControllerTest : SysuiTestCase() {
@Test
fun themeChanged_verifyClockPaletteUpdated() {
clockEventController.clock = clock
- verify(events).onColorPaletteChanged(any(), any(), any())
+ verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
+ verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
clockEventController.registerListeners()
@@ -115,13 +127,14 @@ class ClockEventControllerTest : SysuiTestCase() {
verify(configurationController).addCallback(capture(captor))
captor.value.onThemeChanged()
- verify(events, times(2)).onColorPaletteChanged(any(), any(), any())
+ verify(events).onColorPaletteChanged(any())
}
@Test
fun fontChanged_verifyFontSizeUpdated() {
clockEventController.clock = clock
- verify(events).onColorPaletteChanged(any(), any(), any())
+ verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
+ verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
clockEventController.registerListeners()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 635ee9ea1a2f..400caa3a352a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -45,7 +45,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.plugins.Clock;
+import com.android.systemui.plugins.ClockController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.shared.clocks.ClockRegistry;
@@ -87,7 +87,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@Mock
KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock
- private Clock mClock;
+ private ClockController mClock;
@Mock
DumpManager mDumpManager;
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index a0295d09826f..254f9531ef83 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -41,7 +41,8 @@ import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.Clock;
+import com.android.systemui.plugins.ClockController;
+import com.android.systemui.plugins.ClockFaceController;
import com.android.systemui.statusbar.StatusBarState;
import org.junit.Before;
@@ -61,7 +62,13 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
ViewGroup mMockKeyguardSliceView;
@Mock
- Clock mClock;
+ ClockController mClock;
+
+ @Mock
+ ClockFaceController mSmallClock;
+
+ @Mock
+ ClockFaceController mLargeClock;
private FrameLayout mSmallClockFrame;
private FrameLayout mLargeClockFrame;
@@ -75,8 +82,11 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area))
.thenReturn(mMockKeyguardSliceView);
- when(mClock.getSmallClock()).thenReturn(new TextView(getContext()));
- when(mClock.getLargeClock()).thenReturn(new TextView(getContext()));
+ when(mClock.getSmallClock()).thenReturn(mSmallClock);
+ when(mClock.getLargeClock()).thenReturn(mLargeClock);
+
+ when(mSmallClock.getView()).thenReturn(new TextView(getContext()));
+ when(mLargeClock.getView()).thenReturn(new TextView(getContext()));
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
@@ -124,41 +134,49 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
public void onPluginConnected_showClock() {
mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
- assertEquals(mClock.getSmallClock().getParent(), mSmallClockFrame);
- assertEquals(mClock.getLargeClock().getParent(), mLargeClockFrame);
+ assertEquals(mClock.getSmallClock().getView().getParent(), mSmallClockFrame);
+ assertEquals(mClock.getLargeClock().getView().getParent(), mLargeClockFrame);
}
@Test
public void onPluginConnected_showSecondPluginClock() {
// GIVEN a plugin has already connected
- Clock otherClock = mock(Clock.class);
- when(otherClock.getSmallClock()).thenReturn(new TextView(getContext()));
- when(otherClock.getLargeClock()).thenReturn(new TextView(getContext()));
+ ClockController otherClock = mock(ClockController.class);
+ ClockFaceController smallClock = mock(ClockFaceController.class);
+ ClockFaceController largeClock = mock(ClockFaceController.class);
+ when(otherClock.getSmallClock()).thenReturn(smallClock);
+ when(otherClock.getLargeClock()).thenReturn(largeClock);
+ when(smallClock.getView()).thenReturn(new TextView(getContext()));
+ when(largeClock.getView()).thenReturn(new TextView(getContext()));
mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD);
// THEN only the view from the second plugin should be a child of KeyguardClockSwitch.
- assertThat(otherClock.getSmallClock().getParent()).isEqualTo(mSmallClockFrame);
- assertThat(otherClock.getLargeClock().getParent()).isEqualTo(mLargeClockFrame);
- assertThat(mClock.getSmallClock().getParent()).isNull();
- assertThat(mClock.getLargeClock().getParent()).isNull();
+ assertThat(otherClock.getSmallClock().getView().getParent()).isEqualTo(mSmallClockFrame);
+ assertThat(otherClock.getLargeClock().getView().getParent()).isEqualTo(mLargeClockFrame);
+ assertThat(mClock.getSmallClock().getView().getParent()).isNull();
+ assertThat(mClock.getLargeClock().getView().getParent()).isNull();
}
@Test
public void onPluginDisconnected_secondOfTwoDisconnected() {
// GIVEN two plugins are connected
- Clock otherClock = mock(Clock.class);
- when(otherClock.getSmallClock()).thenReturn(new TextView(getContext()));
- when(otherClock.getLargeClock()).thenReturn(new TextView(getContext()));
+ ClockController otherClock = mock(ClockController.class);
+ ClockFaceController smallClock = mock(ClockFaceController.class);
+ ClockFaceController largeClock = mock(ClockFaceController.class);
+ when(otherClock.getSmallClock()).thenReturn(smallClock);
+ when(otherClock.getLargeClock()).thenReturn(largeClock);
+ when(smallClock.getView()).thenReturn(new TextView(getContext()));
+ when(largeClock.getView()).thenReturn(new TextView(getContext()));
mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD);
mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
// WHEN the second plugin is disconnected
mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
// THEN nothing should be shown
- assertThat(otherClock.getSmallClock().getParent()).isNull();
- assertThat(otherClock.getLargeClock().getParent()).isNull();
- assertThat(mClock.getSmallClock().getParent()).isNull();
- assertThat(mClock.getLargeClock().getParent()).isNull();
+ assertThat(otherClock.getSmallClock().getView().getParent()).isNull();
+ assertThat(otherClock.getLargeClock().getView().getParent()).isNull();
+ assertThat(mClock.getSmallClock().getView().getParent()).isNull();
+ assertThat(mClock.getLargeClock().getView().getParent()).isNull();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index c1036e356cfa..52f8825c724b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -21,10 +21,14 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE;
import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
+import static androidx.constraintlayout.widget.ConstraintSet.CHAIN_SPREAD;
+import static androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT;
+import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
+import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT;
+
import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
@@ -32,9 +36,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,19 +44,17 @@ import android.content.res.Configuration;
import android.graphics.Insets;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
-import android.widget.FrameLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.user.data.source.UserRecord;
import com.android.systemui.util.settings.GlobalSettings;
@@ -64,8 +63,6 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -78,14 +75,11 @@ import java.util.ArrayList;
public class KeyguardSecurityContainerTest extends SysuiTestCase {
private static final int VIEW_WIDTH = 1600;
-
- private int mScreenWidth;
- private int mFakeMeasureSpec;
+ private static final int VIEW_HEIGHT = 900;
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
- @Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
@Mock
private GlobalSettings mGlobalSettings;
@@ -93,59 +87,32 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
private FalsingManager mFalsingManager;
@Mock
private UserSwitcherController mUserSwitcherController;
- @Mock
- private KeyguardStateController mKeyguardStateController;
- @Captor
- private ArgumentCaptor<FrameLayout.LayoutParams> mLayoutCaptor;
private KeyguardSecurityContainer mKeyguardSecurityContainer;
- private FrameLayout.LayoutParams mSecurityViewFlipperLayoutParams;
@Before
public void setup() {
// Needed here, otherwise when mKeyguardSecurityContainer is created below, it'll cache
// the real references (rather than the TestableResources that this call creates).
mContext.ensureTestableResources();
- mSecurityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
- MATCH_PARENT, MATCH_PARENT);
- when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
+ mSecurityViewFlipper = new KeyguardSecurityViewFlipper(getContext());
+ mSecurityViewFlipper.setId(View.generateViewId());
mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
+ mKeyguardSecurityContainer.setRight(VIEW_WIDTH);
+ mKeyguardSecurityContainer.setLeft(0);
+ mKeyguardSecurityContainer.setTop(0);
+ mKeyguardSecurityContainer.setBottom(VIEW_HEIGHT);
+ mKeyguardSecurityContainer.setId(View.generateViewId());
mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
mKeyguardSecurityContainer.addView(mSecurityViewFlipper, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
when(mUserSwitcherController.getCurrentUserName()).thenReturn("Test User");
when(mUserSwitcherController.isKeyguardShowing()).thenReturn(true);
-
- mScreenWidth = getUiDevice().getDisplayWidth();
- mFakeMeasureSpec = View
- .MeasureSpec.makeMeasureSpec(mScreenWidth, View.MeasureSpec.EXACTLY);
- }
-
- @Test
- public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
- mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
- mUserSwitcherController);
-
- int halfWidthMeasureSpec =
- View.MeasureSpec.makeMeasureSpec(mScreenWidth / 2, View.MeasureSpec.EXACTLY);
- mKeyguardSecurityContainer.onMeasure(mFakeMeasureSpec, mFakeMeasureSpec);
-
- verify(mSecurityViewFlipper).measure(halfWidthMeasureSpec, mFakeMeasureSpec);
}
-
- @Test
- public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
- mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
- mUserSwitcherController);
-
- mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
- verify(mSecurityViewFlipper).measure(mFakeMeasureSpec, mFakeMeasureSpec);
- }
-
@Test
- public void onMeasure_respectsViewInsets() {
+ public void testOnApplyWindowInsets() {
int paddingBottom = getContext().getResources()
.getDimensionPixelSize(R.dimen.keyguard_security_view_bottom_margin);
int imeInsetAmount = paddingBottom + 1;
@@ -162,17 +129,12 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
.setInsetsIgnoringVisibility(systemBars(), systemBarInset)
.build();
- // It's reduced by the max of the systembar and IME, so just subtract IME inset.
- int expectedHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
- mScreenWidth - imeInsetAmount, View.MeasureSpec.EXACTLY);
-
mKeyguardSecurityContainer.onApplyWindowInsets(insets);
- mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
- verify(mSecurityViewFlipper).measure(mFakeMeasureSpec, expectedHeightMeasureSpec);
+ assertThat(mKeyguardSecurityContainer.getPaddingBottom()).isEqualTo(imeInsetAmount);
}
@Test
- public void onMeasure_respectsViewInsets_largerSystembar() {
+ public void testOnApplyWindowInsets_largerSystembar() {
int imeInsetAmount = 0;
int paddingBottom = getContext().getResources()
.getDimensionPixelSize(R.dimen.keyguard_security_view_bottom_margin);
@@ -189,25 +151,22 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
.setInsetsIgnoringVisibility(systemBars(), systemBarInset)
.build();
- int expectedHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
- mScreenWidth - systemBarInsetAmount, View.MeasureSpec.EXACTLY);
-
mKeyguardSecurityContainer.onApplyWindowInsets(insets);
- mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
- verify(mSecurityViewFlipper).measure(mFakeMeasureSpec, expectedHeightMeasureSpec);
+ assertThat(mKeyguardSecurityContainer.getPaddingBottom()).isEqualTo(systemBarInsetAmount);
}
- private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
- int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
- mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingManager,
+ @Test
+ public void testDefaultViewMode() {
+ mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
-
- mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
- mKeyguardSecurityContainer.layout(0, 0, mScreenWidth, mScreenWidth);
-
- // Clear any interactions with the mock so we know the interactions definitely come from the
- // below testing.
- reset(mSecurityViewFlipper);
+ mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
+ ConstraintSet.Constraint viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.startToStart).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.endToEnd).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
}
@Test
@@ -217,13 +176,22 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
mKeyguardSecurityContainer.getWidth() - 1f);
verify(mGlobalSettings).putInt(ONE_HANDED_KEYGUARD_SIDE, ONE_HANDED_KEYGUARD_SIDE_RIGHT);
- assertSecurityTranslationX(
- mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
+ ConstraintSet.Constraint viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.widthPercent).isEqualTo(0.5f);
+ assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(-1);
mKeyguardSecurityContainer.updatePositionByTouchX(1f);
verify(mGlobalSettings).putInt(ONE_HANDED_KEYGUARD_SIDE, ONE_HANDED_KEYGUARD_SIDE_LEFT);
- verify(mSecurityViewFlipper).setTranslationX(0.0f);
+ viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.widthPercent).isEqualTo(0.5f);
+ assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(-1);
}
@Test
@@ -232,10 +200,16 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
mKeyguardSecurityContainer.updatePositionByTouchX(
mKeyguardSecurityContainer.getWidth() - 1f);
- verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
+ ConstraintSet.Constraint viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(-1);
+ assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(-1);
mKeyguardSecurityContainer.updatePositionByTouchX(1f);
- verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
+ viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(-1);
+ assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(-1);
}
@Test
@@ -249,17 +223,31 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
setupUserSwitcher();
mKeyguardSecurityContainer.onConfigurationChanged(landscapeConfig);
- // THEN views are oriented side by side
- assertSecurityGravity(Gravity.LEFT | Gravity.BOTTOM);
- assertUserSwitcherGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
- assertSecurityTranslationX(
- mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
- assertUserSwitcherTranslationX(0f);
-
+ ConstraintSet.Constraint viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ ConstraintSet.Constraint userSwitcherConstraint =
+ getViewConstraint(R.id.keyguard_bouncer_user_switcher);
+ assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.leftToRight).isEqualTo(
+ R.id.keyguard_bouncer_user_switcher);
+ assertThat(userSwitcherConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.rightToLeft).isEqualTo(
+ mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.bottomMargin).isEqualTo(
+ getContext().getResources().getDimensionPixelSize(
+ R.dimen.bouncer_user_switcher_y_trans));
+ assertThat(viewFlipperConstraint.layout.horizontalChainStyle).isEqualTo(CHAIN_SPREAD);
+ assertThat(userSwitcherConstraint.layout.horizontalChainStyle).isEqualTo(CHAIN_SPREAD);
+ assertThat(viewFlipperConstraint.layout.mHeight).isEqualTo(MATCH_CONSTRAINT);
+ assertThat(userSwitcherConstraint.layout.mHeight).isEqualTo(MATCH_CONSTRAINT);
}
@Test
- public void testUserSwitcherModeViewGravityPortrait() {
+ public void testUserSwitcherModeViewPositionPortrait() {
// GIVEN one user has been setup and in landscape
when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
Configuration portraitConfig = configuration(ORIENTATION_PORTRAIT);
@@ -267,15 +255,28 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
// WHEN UserSwitcherViewMode is initialized and config has changed
setupUserSwitcher();
- reset(mSecurityViewFlipper);
- when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
mKeyguardSecurityContainer.onConfigurationChanged(portraitConfig);
- // THEN views are both centered horizontally
- assertSecurityGravity(Gravity.CENTER_HORIZONTAL);
- assertUserSwitcherGravity(Gravity.CENTER_HORIZONTAL);
- assertSecurityTranslationX(0);
- assertUserSwitcherTranslationX(0);
+ ConstraintSet.Constraint viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ ConstraintSet.Constraint userSwitcherConstraint =
+ getViewConstraint(R.id.keyguard_bouncer_user_switcher);
+
+ assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.topMargin).isEqualTo(
+ getContext().getResources().getDimensionPixelSize(
+ R.dimen.bouncer_user_switcher_y_trans));
+ assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.rightToRight).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.verticalChainStyle).isEqualTo(CHAIN_SPREAD);
+ assertThat(userSwitcherConstraint.layout.verticalChainStyle).isEqualTo(CHAIN_SPREAD);
+ assertThat(viewFlipperConstraint.layout.mHeight).isEqualTo(MATCH_CONSTRAINT);
+ assertThat(userSwitcherConstraint.layout.mHeight).isEqualTo(WRAP_CONTENT);
+ assertThat(userSwitcherConstraint.layout.mWidth).isEqualTo(WRAP_CONTENT);
}
@Test
@@ -337,9 +338,9 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_LEFT);
mKeyguardSecurityContainer.onConfigurationChanged(new Configuration());
- assertSecurityTranslationX(0);
- assertUserSwitcherTranslationX(
- mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
+ ConstraintSet.Constraint viewFlipperConstraint = getViewConstraint(
+ mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
}
private Configuration configuration(@Configuration.Orientation int orientation) {
@@ -348,28 +349,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
return config;
}
- private void assertSecurityTranslationX(float translation) {
- verify(mSecurityViewFlipper).setTranslationX(translation);
- }
-
- private void assertUserSwitcherTranslationX(float translation) {
- ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
- R.id.keyguard_bouncer_user_switcher);
- assertThat(userSwitcher.getTranslationX()).isEqualTo(translation);
- }
-
- private void assertUserSwitcherGravity(@Gravity.GravityFlags int gravity) {
- ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
- R.id.keyguard_bouncer_user_switcher);
- assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
- .isEqualTo(gravity);
- }
-
- private void assertSecurityGravity(@Gravity.GravityFlags int gravity) {
- verify(mSecurityViewFlipper, atLeastOnce()).setLayoutParams(mLayoutCaptor.capture());
- assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(gravity);
- }
-
private void setViewWidth(int width) {
mKeyguardSecurityContainer.setRight(width);
mKeyguardSecurityContainer.setLeft(0);
@@ -399,9 +378,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_RIGHT);
mKeyguardSecurityContainer.initMode(KeyguardSecurityContainer.MODE_USER_SWITCHER,
mGlobalSettings, mFalsingManager, mUserSwitcherController);
- // reset mSecurityViewFlipper so setup doesn't influence test verifications
- reset(mSecurityViewFlipper);
- when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
}
private ArrayList<UserRecord> buildUserRecords(int count) {
@@ -415,4 +391,17 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
}
return users;
}
+
+ private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
+ int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
+ mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
+ }
+
+ /** Get the ConstraintLayout constraint of the view. */
+ private ConstraintSet.Constraint getViewConstraint(int viewId) {
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(mKeyguardSecurityContainer);
+ return constraintSet.getConstraint(viewId);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 8ca17b974100..19a6c66652dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -19,6 +19,8 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+import static com.android.systemui.flags.Flags.A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -27,10 +29,12 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.ContextWrapper;
+import android.hardware.display.DisplayManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -40,6 +44,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import com.android.systemui.flags.FakeFeatureFlags;
import org.junit.After;
import org.junit.Before;
@@ -53,7 +58,7 @@ import org.mockito.junit.MockitoRule;
/** Test for {@link AccessibilityFloatingMenuController}. */
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@@ -70,6 +75,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor;
private KeyguardUpdateMonitorCallback mKeyguardCallback;
+ private int mLastButtonMode;
+ private String mLastButtonTargets;
@Before
public void setUp() throws Exception {
@@ -79,6 +86,11 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
return getBaseContext();
}
};
+
+ mLastButtonTargets = Settings.Secure.getStringForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, UserHandle.USER_CURRENT);
+ mLastButtonMode = Settings.Secure.getIntForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, UserHandle.USER_CURRENT);
}
@After
@@ -87,6 +99,13 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
mController.onAccessibilityButtonTargetsChanged("");
mController = null;
}
+
+ Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, mLastButtonTargets,
+ UserHandle.USER_CURRENT);
+ Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mLastButtonMode,
+ UserHandle.USER_CURRENT);
}
@Test
@@ -287,13 +306,50 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
assertThat(mController.mFloatingMenu).isNull();
}
+ @Test
+ public void onTargetsChanged_flingSpringAnimationsDisabled_floatingMenuIsCreated() {
+ Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
+ UserHandle.USER_CURRENT);
+ final FakeFeatureFlags featureFlags = new FakeFeatureFlags();
+ featureFlags.set(A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS, false);
+
+ mController = setUpController();
+ mController.onAccessibilityButtonTargetsChanged(TEST_A11Y_BTN_TARGETS);
+
+ assertThat(mController.mFloatingMenu).isInstanceOf(AccessibilityFloatingMenu.class);
+ }
+
+ @Test
+ public void onTargetsChanged_isFloatingViewLayerControllerCreated() {
+ Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
+ UserHandle.USER_CURRENT);
+ final FakeFeatureFlags featureFlags = new FakeFeatureFlags();
+ featureFlags.set(A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS, true);
+
+ mController = setUpController(featureFlags);
+ mController.onAccessibilityButtonTargetsChanged(TEST_A11Y_BTN_TARGETS);
+
+ assertThat(mController.mFloatingMenu).isInstanceOf(MenuViewLayerController.class);
+ }
+
private AccessibilityFloatingMenuController setUpController() {
+ final FakeFeatureFlags featureFlags = new FakeFeatureFlags();
+ featureFlags.set(A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS, false);
+ return setUpController(featureFlags);
+ }
+
+ private AccessibilityFloatingMenuController setUpController(FakeFeatureFlags featureFlags) {
+ final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
mTargetsObserver = spy(Dependency.get(AccessibilityButtonTargetsObserver.class));
mModeObserver = spy(Dependency.get(AccessibilityButtonModeObserver.class));
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
final AccessibilityFloatingMenuController controller =
- new AccessibilityFloatingMenuController(mContextWrapper, mTargetsObserver,
- mModeObserver, mKeyguardUpdateMonitor);
+ new AccessibilityFloatingMenuController(mContextWrapper, windowManager,
+ displayManager, mTargetsObserver, mModeObserver, mKeyguardUpdateMonitor,
+ featureFlags);
controller.init();
return controller;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
new file mode 100644
index 000000000000..d8b10e04705e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 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.accessibility.floatingmenu;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/** Tests for {@link MenuInfoRepository}. */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class MenuInfoRepositoryTest extends SysuiTestCase {
+ @Rule
+ public MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private MenuInfoRepository.OnSettingsContentsChanged mMockSettingsContentsChanged;
+
+ @Test
+ public void menuSizeTypeChanged_verifyOnSizeTypeChanged() {
+ final MenuInfoRepository menuInfoRepository =
+ new MenuInfoRepository(mContext, mMockSettingsContentsChanged);
+
+ menuInfoRepository.mMenuSizeContentObserver.onChange(true);
+
+ verify(mMockSettingsContentsChanged).onSizeTypeChanged(anyInt());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
new file mode 100644
index 000000000000..f782a446c627
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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.accessibility.floatingmenu;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/** Tests for {@link MenuViewLayerController}. */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class MenuViewLayerControllerTest extends SysuiTestCase {
+ @Rule
+ public MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private WindowManager mWindowManager;
+
+ private MenuViewLayerController mMenuViewLayerController;
+
+ @Before
+ public void setUp() throws Exception {
+ mMenuViewLayerController = new MenuViewLayerController(mContext, mWindowManager);
+ }
+
+ @Test
+ public void show_shouldAddViewToWindow() {
+ mMenuViewLayerController.show();
+
+ verify(mWindowManager).addView(any(View.class), any(ViewGroup.LayoutParams.class));
+ }
+
+ @Test
+ public void hide_menuIsShowing_removeViewFromWindow() {
+ mMenuViewLayerController.show();
+
+ mMenuViewLayerController.hide();
+
+ verify(mWindowManager).removeView(any(View.class));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
new file mode 100644
index 000000000000..8883cb783438
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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.accessibility.floatingmenu;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link MenuViewLayer}. */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class MenuViewLayerTest extends SysuiTestCase {
+ private MenuViewLayer mMenuViewLayer;
+
+ @Before
+ public void setUp() throws Exception {
+ mMenuViewLayer = new MenuViewLayer(mContext);
+ }
+
+ @Test
+ public void onAttachedToWindow_menuIsVisible() {
+ mMenuViewLayer.onAttachedToWindow();
+ final View menuView = mMenuViewLayer.getChildAt(LayerIndex.MENU_VIEW);
+
+ assertThat(menuView.getVisibility()).isEqualTo(VISIBLE);
+ }
+
+ @Test
+ public void onAttachedToWindow_menuIsGone() {
+ mMenuViewLayer.onDetachedFromWindow();
+ final View menuView = mMenuViewLayer.getChildAt(LayerIndex.MENU_VIEW);
+
+ assertThat(menuView.getVisibility()).isEqualTo(GONE);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
new file mode 100644
index 000000000000..513044d2c20d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 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.accessibility.floatingmenu;
+
+import static android.app.UiModeManager.MODE_NIGHT_YES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.UiModeManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link MenuView}. */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class MenuViewTest extends SysuiTestCase {
+ private static final int INDEX_MENU_ITEM = 0;
+ private int mNightMode;
+ private UiModeManager mUiModeManager;
+ private MenuView mMenuView;
+
+ @Before
+ public void setUp() throws Exception {
+ mUiModeManager = mContext.getSystemService(UiModeManager.class);
+ mNightMode = mUiModeManager.getNightMode();
+ mUiModeManager.setNightMode(MODE_NIGHT_YES);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext);
+ final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext);
+ mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance));
+ }
+
+ @Test
+ public void onConfigurationChanged_updateViewModel() {
+ mMenuView.onConfigurationChanged(/* newConfig= */ null);
+
+ verify(mMenuView).loadLayoutResources();
+ }
+
+ @Test
+ public void insetsOnDarkTheme_menuOnLeft_matchInsets() {
+ mMenuView.onConfigurationChanged(/* newConfig= */ null);
+ final InstantInsetLayerDrawable insetLayerDrawable =
+ (InstantInsetLayerDrawable) mMenuView.getBackground();
+ final boolean areInsetsMatched = insetLayerDrawable.getLayerInsetLeft(INDEX_MENU_ITEM) != 0
+ && insetLayerDrawable.getLayerInsetRight(INDEX_MENU_ITEM) == 0;
+
+ assertThat(areInsetsMatched).isTrue();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mUiModeManager.setNightMode(mNightMode);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index c0acc71880b5..8e45067c8e13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -160,6 +160,8 @@ public class AuthControllerTest extends SysuiTestCase {
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
+ private UdfpsLogger mUdfpsLogger;
+ @Mock
private InteractionJankMonitor mInteractionJankMonitor;
@Captor
private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor;
@@ -978,7 +980,7 @@ public class AuthControllerTest extends SysuiTestCase {
super(context, execution, commandQueue, activityTaskManager, windowManager,
fingerprintManager, faceManager, udfpsControllerFactory,
sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle,
- mUserManager, mLockPatternUtils, statusBarStateController,
+ mUserManager, mLockPatternUtils, mUdfpsLogger, statusBarStateController,
mInteractionJankMonitor, mHandler, mBackgroundExecutor, vibratorHelper);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 3be1a56b4544..11e58801bb7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -126,8 +126,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private WindowManager mWindowManager;
@Mock
- private UdfpsDisplayModeProvider mDisplayModeProvider;
- @Mock
private StatusBarStateController mStatusBarStateController;
@Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -168,6 +166,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private LatencyTracker mLatencyTracker;
private FakeExecutor mFgExecutor;
+ @Mock
+ private UdfpsDisplayMode mUdfpsDisplayMode;
// Stuff for configuring mocks
@Mock
@@ -257,7 +257,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
mVibrator,
mUdfpsHapticsSimulator,
mUdfpsShell,
- Optional.of(mDisplayModeProvider),
mKeyguardStateController,
mDisplayManager,
mHandler,
@@ -274,6 +273,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
mScreenObserver = mScreenObserverCaptor.getValue();
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, new UdfpsOverlayParams());
+ mUdfpsController.setUdfpsDisplayMode(mUdfpsDisplayMode);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
new file mode 100644
index 000000000000..7864f21bd1d6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.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.systemui.biometrics;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.fingerprint.IUdfpsHbmListener;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecution;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+public class UdfpsDisplayModeTest extends SysuiTestCase {
+ private static final int DISPLAY_ID = 0;
+
+ @Mock
+ private AuthController mAuthController;
+ @Mock
+ private IUdfpsHbmListener mDisplayCallback;
+ @Mock
+ private UdfpsLogger mUdfpsLogger;
+ @Mock
+ private Runnable mOnEnabled;
+ @Mock
+ private Runnable mOnDisabled;
+
+ private final FakeExecution mExecution = new FakeExecution();
+ private UdfpsDisplayMode mHbmController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ // Force mContext to always return DISPLAY_ID
+ Context contextSpy = spy(mContext);
+ when(contextSpy.getDisplayId()).thenReturn(DISPLAY_ID);
+
+ // Set up mocks.
+ when(mAuthController.getUdfpsHbmListener()).thenReturn(mDisplayCallback);
+
+ // Create a real controller with mock dependencies.
+ mHbmController = new UdfpsDisplayMode(contextSpy, mExecution, mAuthController,
+ mUdfpsLogger);
+ }
+
+ @Test
+ public void roundTrip() throws RemoteException {
+ // Enable the UDFPS mode.
+ mHbmController.enable(mOnEnabled);
+
+ // Should set the appropriate refresh rate for UDFPS and notify the caller.
+ verify(mDisplayCallback).onHbmEnabled(eq(DISPLAY_ID));
+ verify(mOnEnabled).run();
+
+ // Disable the UDFPS mode.
+ mHbmController.disable(mOnDisabled);
+
+ // Should unset the refresh rate and notify the caller.
+ verify(mOnDisabled).run();
+ verify(mDisplayCallback).onHbmDisabled(eq(DISPLAY_ID));
+ }
+
+ @Test
+ public void mustNotEnableMoreThanOnce() throws RemoteException {
+ // First request to enable the UDFPS mode.
+ mHbmController.enable(mOnEnabled);
+
+ // Should set the appropriate refresh rate for UDFPS and notify the caller.
+ verify(mDisplayCallback).onHbmEnabled(eq(DISPLAY_ID));
+ verify(mOnEnabled).run();
+
+ // Second request to enable the UDFPS mode, while it's still enabled.
+ mHbmController.enable(mOnEnabled);
+
+ // Should ignore the second request.
+ verifyNoMoreInteractions(mDisplayCallback);
+ verifyNoMoreInteractions(mOnEnabled);
+ }
+
+ @Test
+ public void mustNotDisableMoreThanOnce() throws RemoteException {
+ // Disable the UDFPS mode.
+ mHbmController.enable(mOnEnabled);
+
+ // Should set the appropriate refresh rate for UDFPS and notify the caller.
+ verify(mDisplayCallback).onHbmEnabled(eq(DISPLAY_ID));
+ verify(mOnEnabled).run();
+
+ // First request to disable the UDFPS mode.
+ mHbmController.disable(mOnDisabled);
+
+ // Should unset the refresh rate and notify the caller.
+ verify(mOnDisabled).run();
+ verify(mDisplayCallback).onHbmDisabled(eq(DISPLAY_ID));
+
+ // Second request to disable the UDFPS mode, when it's already disabled.
+ mHbmController.disable(mOnDisabled);
+
+ // Should ignore the second request.
+ verifyNoMoreInteractions(mOnDisabled);
+ verifyNoMoreInteractions(mDisplayCallback);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
index 4511193d41d3..20a82c63cfdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -21,15 +21,10 @@ import android.content.Intent
import android.content.pm.PackageManager.NameNotFoundException
import android.content.res.Resources
import android.test.suitebuilder.annotation.SmallTest
-import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.DeviceConfigProxyFake
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.withArgCaptor
@@ -46,18 +41,16 @@ import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyString
-import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
/**
- * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
- * overriding, and should never return any value other than the one provided as the default.
+ * NOTE: This test is for the version of FeatureFlagManager in src-debug, which allows overriding
+ * the default.
*/
@SmallTest
class FeatureFlagsDebugTest : SysuiTestCase() {
@@ -68,10 +61,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Mock private lateinit var secureSettings: SecureSettings
@Mock private lateinit var systemProperties: SystemPropertiesHelper
@Mock private lateinit var resources: Resources
- @Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var commandRegistry: CommandRegistry
- @Mock private lateinit var barService: IStatusBarService
- @Mock private lateinit var pw: PrintWriter
+ @Mock private lateinit var restarter: Restarter
private val flagMap = mutableMapOf<Int, Flag<*>>()
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var clearCacheAction: Consumer<Int>
@@ -92,12 +83,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
secureSettings,
systemProperties,
resources,
- dumpManager,
deviceConfig,
serverFlagReader,
flagMap,
- commandRegistry,
- barService
+ restarter
)
verify(flagManager).onSettingsChangedAction = any()
broadcastReceiver = withArgCaptor {
@@ -366,53 +355,6 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun statusBarCommand_IsRegistered() {
- verify(commandRegistry).registerCommand(anyString(), any())
- }
-
- @Test
- fun noOpCommand() {
- val cmd = captureCommand()
-
- cmd.execute(pw, ArrayList())
- verify(pw, atLeastOnce()).println()
- verify(flagManager).readFlagValue<Boolean>(eq(1), any())
- verifyZeroInteractions(secureSettings)
- }
-
- @Test
- fun readFlagCommand() {
- addFlag(UnreleasedFlag(1))
- val cmd = captureCommand()
- cmd.execute(pw, listOf("1"))
- verify(flagManager).readFlagValue<Boolean>(eq(1), any())
- }
-
- @Test
- fun setFlagCommand() {
- addFlag(UnreleasedFlag(1))
- val cmd = captureCommand()
- cmd.execute(pw, listOf("1", "on"))
- verifyPutData(1, "{\"type\":\"boolean\",\"value\":true}")
- }
-
- @Test
- fun toggleFlagCommand() {
- addFlag(ReleasedFlag(1))
- val cmd = captureCommand()
- cmd.execute(pw, listOf("1", "toggle"))
- verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}", 2)
- }
-
- @Test
- fun eraseFlagCommand() {
- addFlag(ReleasedFlag(1))
- val cmd = captureCommand()
- cmd.execute(pw, listOf("1", "erase"))
- verify(secureSettings).putStringForUser(eq("key-1"), eq(""), anyInt())
- }
-
- @Test
fun dumpFormat() {
val flag1 = ReleasedFlag(1)
val flag2 = ResourceBooleanFlag(2, 1002)
@@ -471,13 +413,6 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
return flag
}
- private fun captureCommand(): Command {
- val captor = argumentCaptor<Function0<Command>>()
- verify(commandRegistry).registerCommand(anyString(), capture(captor))
-
- return captor.value.invoke()
- }
-
private fun dumpToString(): String {
val sw = StringWriter()
val pw = PrintWriter(sw)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
index e94b5202956d..575c14262b74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -19,17 +19,12 @@ import android.content.pm.PackageManager.NameNotFoundException
import android.content.res.Resources
import android.test.suitebuilder.annotation.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
import com.android.systemui.util.DeviceConfigProxyFake
-import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
-import org.junit.After
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -43,7 +38,6 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
@Mock private lateinit var mResources: Resources
@Mock private lateinit var mSystemProperties: SystemPropertiesHelper
- @Mock private lateinit var mDumpManager: DumpManager
private val serverFlagReader = ServerFlagReaderFake()
private val deviceConfig = DeviceConfigProxyFake()
@@ -55,15 +49,7 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
mResources,
mSystemProperties,
deviceConfig,
- serverFlagReader,
- mDumpManager)
- }
-
- @After
- fun onFinished() {
- // The dump manager should be registered with even for the release version, but that's it.
- verify(mDumpManager).registerDumpable(any(), any())
- verifyNoMoreInteractions(mDumpManager)
+ serverFlagReader)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
new file mode 100644
index 000000000000..4c6113870737
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 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.flags
+
+import android.test.suitebuilder.annotation.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import java.io.PrintWriter
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class FlagCommandTest : SysuiTestCase() {
+
+ @Mock private lateinit var featureFlags: FeatureFlagsDebug
+ @Mock private lateinit var pw: PrintWriter
+ private val flagMap = mutableMapOf<Int, Flag<*>>()
+ private val flagA = UnreleasedFlag(500)
+ private val flagB = ReleasedFlag(501)
+
+ private lateinit var cmd: FlagCommand
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(featureFlags.isEnabled(any(UnreleasedFlag::class.java))).thenReturn(false)
+ whenever(featureFlags.isEnabled(any(ReleasedFlag::class.java))).thenReturn(true)
+ flagMap.put(flagA.id, flagA)
+ flagMap.put(flagB.id, flagB)
+
+ cmd = FlagCommand(featureFlags, flagMap)
+ }
+
+ @Test
+ fun noOpCommand() {
+ cmd.execute(pw, ArrayList())
+ Mockito.verify(pw, Mockito.atLeastOnce()).println()
+ Mockito.verify(featureFlags).isEnabled(flagA)
+ Mockito.verify(featureFlags).isEnabled(flagB)
+ }
+
+ @Test
+ fun readFlagCommand() {
+ cmd.execute(pw, listOf(flagA.id.toString()))
+ Mockito.verify(featureFlags).isEnabled(flagA)
+ }
+
+ @Test
+ fun setFlagCommand() {
+ cmd.execute(pw, listOf(flagB.id.toString(), "on"))
+ Mockito.verify(featureFlags).setBooleanFlagInternal(flagB, true)
+ }
+
+ @Test
+ fun toggleFlagCommand() {
+ cmd.execute(pw, listOf(flagB.id.toString(), "toggle"))
+ Mockito.verify(featureFlags).setBooleanFlagInternal(flagB, false)
+ }
+
+ @Test
+ fun eraseFlagCommand() {
+ cmd.execute(pw, listOf(flagA.id.toString(), "erase"))
+ Mockito.verify(featureFlags).eraseFlag(flagA)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 21c018a0419d..39f3c96803c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -176,7 +176,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
// and the keyguard goes away
mViewMediator.setShowingLocked(false);
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
mViewMediator.mUpdateCallback.onKeyguardVisibilityChanged(false);
TestableLooper.get(this).processAllMessages();
@@ -201,7 +201,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
// and the keyguard goes away
mViewMediator.setShowingLocked(false);
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
mViewMediator.mUpdateCallback.onKeyguardVisibilityChanged(false);
TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 2a130535c657..d82819397f08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -64,6 +64,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
context,
FakeExecutor(FakeSystemClock()),
)
+ mediaTttCommandLineHelper.start()
}
@Test(expected = IllegalStateException::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 775dc11f6edd..9577274eef8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -41,6 +41,7 @@ import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -71,6 +72,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Mock
private lateinit var powerManager: PowerManager
@Mock
+ private lateinit var viewUtil: ViewUtil
+ @Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var commandQueue: CommandQueue
@@ -104,8 +107,10 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
configurationController,
powerManager,
Handler.getMain(),
- receiverUiEventLogger
+ receiverUiEventLogger,
+ viewUtil,
)
+ controllerReceiver.start()
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
verify(commandQueue).addCallback(callbackCaptor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index eca3bedee108..3a8a51d42f77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -47,8 +47,8 @@ import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
-import dagger.Lazy
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -83,13 +83,11 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Mock
private lateinit var commandQueue: CommandQueue
@Mock
- private lateinit var lazyFalsingManager: Lazy<FalsingManager>
- @Mock
private lateinit var falsingManager: FalsingManager
@Mock
- private lateinit var lazyFalsingCollector: Lazy<FalsingCollector>
- @Mock
private lateinit var falsingCollector: FalsingCollector
+ @Mock
+ private lateinit var viewUtil: ViewUtil
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
private lateinit var fakeClock: FakeSystemClock
@@ -116,8 +114,6 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
senderUiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT)
- whenever(lazyFalsingManager.get()).thenReturn(falsingManager)
- whenever(lazyFalsingCollector.get()).thenReturn(falsingCollector)
controllerSender = TestMediaTttChipControllerSender(
commandQueue,
@@ -129,9 +125,11 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
configurationController,
powerManager,
senderUiEventLogger,
- lazyFalsingManager,
- lazyFalsingCollector
+ falsingManager,
+ falsingCollector,
+ viewUtil,
)
+ controllerSender.start()
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
verify(commandQueue).addCallback(callbackCaptor.capture())
@@ -437,7 +435,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun transferToReceiverSucceeded_withUndoRunnable_falseTap_callbackNotRun() {
- whenever(lazyFalsingManager.get().isFalseTap(anyInt())).thenReturn(true)
+ whenever(falsingManager.isFalseTap(anyInt())).thenReturn(true)
var undoCallbackCalled = false
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {
@@ -453,7 +451,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun transferToReceiverSucceeded_withUndoRunnable_realTap_callbackRun() {
- whenever(lazyFalsingManager.get().isFalseTap(anyInt())).thenReturn(false)
+ whenever(falsingManager.isFalseTap(anyInt())).thenReturn(false)
var undoCallbackCalled = false
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {
@@ -835,8 +833,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
configurationController: ConfigurationController,
powerManager: PowerManager,
uiEventLogger: MediaTttSenderUiEventLogger,
- falsingManager: Lazy<FalsingManager>,
- falsingCollector: Lazy<FalsingCollector>,
+ falsingManager: FalsingManager,
+ falsingCollector: FalsingCollector,
+ viewUtil: ViewUtil,
) : MediaTttChipControllerSender(
commandQueue,
context,
@@ -849,6 +848,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
uiEventLogger,
falsingManager,
falsingCollector,
+ viewUtil,
) {
override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
// Just bypass the animation in tests
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index 80731037481a..6c03730e056e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -39,7 +39,6 @@ import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.keyguard.KeyguardViewController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -49,6 +48,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -113,7 +113,7 @@ public class NavBarHelperTest extends SysuiTestCase {
mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
mSystemActions, mOverviewProxyService, mAssistManagerLazy,
- () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardViewController.class),
+ () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class),
mNavigationModeController, mUserTracker, mDumpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index b0cf0612b2d2..9bf27a26a682 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -19,6 +19,8 @@ package com.android.systemui.navigationbar;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -30,20 +32,25 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarController;
@@ -72,11 +79,14 @@ public class NavigationBarControllerTest extends SysuiTestCase {
private NavigationBarController mNavigationBarController;
private NavigationBar mDefaultNavBar;
private NavigationBar mSecondaryNavBar;
+ private StaticMockitoSession mMockitoSession;
@Mock
private CommandQueue mCommandQueue;
@Mock
private NavigationBarComponent.Factory mNavigationBarFactory;
+ @Mock
+ TaskbarDelegate mTaskbarDelegate;
@Before
public void setUp() {
@@ -90,7 +100,7 @@ public class NavigationBarControllerTest extends SysuiTestCase {
Dependency.get(Dependency.MAIN_HANDLER),
mock(ConfigurationController.class),
mock(NavBarHelper.class),
- mock(TaskbarDelegate.class),
+ mTaskbarDelegate,
mNavigationBarFactory,
mock(StatusBarKeyguardViewManager.class),
mock(DumpManager.class),
@@ -100,6 +110,7 @@ public class NavigationBarControllerTest extends SysuiTestCase {
Optional.of(mock(BackAnimation.class)),
mock(FeatureFlags.class)));
initializeNavigationBars();
+ mMockitoSession = mockitoSession().mockStatic(Utilities.class).startMocking();
}
private void initializeNavigationBars() {
@@ -120,6 +131,7 @@ public class NavigationBarControllerTest extends SysuiTestCase {
mNavigationBarController = null;
mDefaultNavBar = null;
mSecondaryNavBar = null;
+ mMockitoSession.finishMocking();
}
@Test
@@ -268,4 +280,22 @@ public class NavigationBarControllerTest extends SysuiTestCase {
public void test3ButtonTaskbarFlagDisabledNoRegister() {
verify(mCommandQueue, never()).addCallback(any(TaskbarDelegate.class));
}
+
+
+ @Test
+ public void testConfigurationChange_taskbarNotInitialized() {
+ Configuration configuration = mContext.getResources().getConfiguration();
+ when(Utilities.isTablet(any())).thenReturn(true);
+ mNavigationBarController.onConfigChanged(configuration);
+ verify(mTaskbarDelegate, never()).onConfigurationChanged(configuration);
+ }
+
+ @Test
+ public void testConfigurationChange_taskbarInitialized() {
+ Configuration configuration = mContext.getResources().getConfiguration();
+ when(Utilities.isTablet(any())).thenReturn(true);
+ when(mTaskbarDelegate.isInitialized()).thenReturn(true);
+ mNavigationBarController.onConfigChanged(configuration);
+ verify(mTaskbarDelegate, times(1)).onConfigurationChanged(configuration);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 51f0953771cb..0e9d2799dddb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -72,7 +72,6 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
-import com.android.keyguard.KeyguardViewController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
@@ -194,7 +193,7 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
private CentralSurfaces mCentralSurfaces;
@Mock
- private KeyguardViewController mKeyguardViewController;
+ private KeyguardStateController mKeyguardStateController;
@Mock
private UserContextProvider mUserContextProvider;
@Mock
@@ -240,7 +239,7 @@ public class NavigationBarTest extends SysuiTestCase {
mock(AccessibilityButtonTargetsObserver.class),
mSystemActions, mOverviewProxyService,
() -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
- mKeyguardViewController, mock(NavigationModeController.class),
+ mKeyguardStateController, mock(NavigationModeController.class),
mock(UserTracker.class), mock(DumpManager.class)));
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
@@ -380,7 +379,7 @@ public class NavigationBarTest extends SysuiTestCase {
// Verify navbar didn't alter and showing back icon when the keyguard is showing without
// requesting IME insets visible.
- doReturn(true).when(mKeyguardViewController).isShowing();
+ doReturn(true).when(mKeyguardStateController).isShowing();
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
index 073c23cec569..5cb27a47d384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -28,7 +28,6 @@ import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler.bundleToHardwareBitmap
import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
@@ -140,66 +139,6 @@ class RequestProcessorTest {
}
@Test
- fun testSelectedRegionScreenshot_workProfilePolicyDisabled() = runBlocking {
- flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
-
- val request = ScreenshotRequest(TAKE_SCREENSHOT_SELECTED_REGION, SCREENSHOT_KEY_CHORD)
- val processor = RequestProcessor(imageCapture, policy, flags, scope)
-
- val processedRequest = processor.process(request)
-
- // No changes
- assertThat(processedRequest).isEqualTo(request)
- }
-
- @Test
- fun testSelectedRegionScreenshot() = runBlocking {
- flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true)
-
- val request = ScreenshotRequest(TAKE_SCREENSHOT_SELECTED_REGION, SCREENSHOT_KEY_CHORD)
- val processor = RequestProcessor(imageCapture, policy, flags, scope)
-
- policy.setManagedProfile(USER_ID, false)
- policy.setDisplayContentInfo(policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID))
-
- val processedRequest = processor.process(request)
-
- // Request has topComponent added, but otherwise unchanged.
- assertThat(processedRequest.type).isEqualTo(TAKE_SCREENSHOT_FULLSCREEN)
- assertThat(processedRequest.topComponent).isEqualTo(component)
- }
-
- @Test
- fun testSelectedRegionScreenshot_managedProfile() = runBlocking {
- flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true)
-
- // Provide a fake task bitmap when asked
- val bitmap = makeHardwareBitmap(100, 100)
- imageCapture.image = bitmap
-
- val request = ScreenshotRequest(TAKE_SCREENSHOT_SELECTED_REGION, SCREENSHOT_KEY_CHORD)
- val processor = RequestProcessor(imageCapture, policy, flags, scope)
-
- // Indicate that the primary content belongs to a manged profile
- policy.setManagedProfile(USER_ID, true)
- policy.setDisplayContentInfo(policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID))
-
- val processedRequest = processor.process(request)
-
- // Expect a task snapshot is taken, overriding the selected region mode
- assertThat(processedRequest.type).isEqualTo(TAKE_SCREENSHOT_PROVIDED_IMAGE)
- assertThat(bitmap.equalsHardwareBitmapBundle(processedRequest.bitmapBundle)).isTrue()
- assertThat(processedRequest.boundsInScreen).isEqualTo(bounds)
- assertThat(processedRequest.insets).isEqualTo(Insets.NONE)
- assertThat(processedRequest.taskId).isEqualTo(TASK_ID)
- assertThat(imageCapture.requestedTaskId).isEqualTo(TASK_ID)
- assertThat(processedRequest.userId).isEqualTo(USER_ID)
- assertThat(processedRequest.topComponent).isEqualTo(component)
- }
-
- @Test
fun testProvidedImageScreenshot_workProfilePolicyDisabled() = runBlocking {
flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 002ef2962b03..3a4da86b8045 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -33,7 +33,6 @@ import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.util.ScreenshotHelper
@@ -175,28 +174,6 @@ class TakeScreenshotServiceTest : SysuiTestCase() {
}
@Test
- fun takeScreenshotPartial() {
- val request = ScreenshotRequest(
- TAKE_SCREENSHOT_SELECTED_REGION,
- SCREENSHOT_KEY_CHORD,
- /* topComponent = */ null)
-
- service.handleRequest(request, { /* onSaved */ }, callback)
-
- verify(controller, times(1)).takeScreenshotPartial(
- /* topComponent = */ isNull(),
- /* onSavedListener = */ any(),
- /* requestCallback = */ any())
-
- assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
- val logEvent = eventLogger.get(0)
-
- assertEquals("Expected SCREENSHOT_REQUESTED UiEvent",
- logEvent.eventId, SCREENSHOT_REQUESTED_KEY_CHORD.id)
- assertEquals("Expected empty package name in UiEvent", "", eventLogger.get(0).packageName)
- }
-
- @Test
fun takeScreenshotProvidedImage() {
val bounds = Rect(50, 50, 150, 150)
val bitmap = makeHardwareBitmap(100, 100)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 8be138a3b2be..ffb41e5378bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -22,7 +22,7 @@ import android.os.Handler
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.Clock
+import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProviderPlugin
@@ -48,8 +48,8 @@ class ClockRegistryTest : SysuiTestCase() {
@JvmField @Rule val mockito = MockitoJUnit.rule()
@Mock private lateinit var mockContext: Context
@Mock private lateinit var mockPluginManager: PluginManager
- @Mock private lateinit var mockClock: Clock
- @Mock private lateinit var mockDefaultClock: Clock
+ @Mock private lateinit var mockClock: ClockController
+ @Mock private lateinit var mockDefaultClock: ClockController
@Mock private lateinit var mockThumbnail: Drawable
@Mock private lateinit var mockHandler: Handler
@Mock private lateinit var mockContentResolver: ContentResolver
@@ -60,7 +60,7 @@ class ClockRegistryTest : SysuiTestCase() {
private var settingValue: String = ""
companion object {
- private fun failFactory(): Clock {
+ private fun failFactory(): ClockController {
fail("Unexpected call to createClock")
return null!!
}
@@ -73,17 +73,17 @@ class ClockRegistryTest : SysuiTestCase() {
private class FakeClockPlugin : ClockProviderPlugin {
private val metadata = mutableListOf<ClockMetadata>()
- private val createCallbacks = mutableMapOf<ClockId, () -> Clock>()
+ private val createCallbacks = mutableMapOf<ClockId, () -> ClockController>()
private val thumbnailCallbacks = mutableMapOf<ClockId, () -> Drawable?>()
override fun getClocks() = metadata
- override fun createClock(id: ClockId): Clock = createCallbacks[id]!!()
+ override fun createClock(id: ClockId): ClockController = createCallbacks[id]!!()
override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!()
fun addClock(
id: ClockId,
name: String,
- create: () -> Clock = ::failFactory,
+ create: () -> ClockController = ::failFactory,
getThumbnail: () -> Drawable? = ::failThumbnail
): FakeClockPlugin {
metadata.add(ClockMetadata(id, name))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index 2b4a109282ce..539a54b731ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.shared.clocks
import android.content.res.Resources
+import android.graphics.Color
import android.graphics.drawable.Drawable
import android.testing.AndroidTestingRunner
import android.util.TypedValue
@@ -25,7 +26,7 @@ import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.shared.clocks.DefaultClock.Companion.DOZE_COLOR
+import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -88,17 +89,20 @@ class DefaultClockProviderTest : SysuiTestCase() {
// Default clock provider must always provide the default clock
val clock = provider.createClock(DEFAULT_CLOCK_ID)
assertNotNull(clock)
- assertEquals(clock.smallClock, mockSmallClockView)
- assertEquals(clock.largeClock, mockLargeClockView)
+ assertEquals(mockSmallClockView, clock.smallClock.view)
+ assertEquals(mockLargeClockView, clock.largeClock.view)
}
@Test
fun defaultClock_initialize() {
val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ verify(mockSmallClockView).setColors(Color.MAGENTA, Color.MAGENTA)
+ verify(mockLargeClockView).setColors(Color.MAGENTA, Color.MAGENTA)
+
clock.initialize(resources, 0f, 0f)
- verify(mockSmallClockView, times(2)).setColors(eq(DOZE_COLOR), anyInt())
- verify(mockLargeClockView, times(2)).setColors(eq(DOZE_COLOR), anyInt())
+ verify(mockSmallClockView).setColors(eq(DOZE_COLOR), anyInt())
+ verify(mockLargeClockView).setColors(eq(DOZE_COLOR), anyInt())
verify(mockSmallClockView).onTimeZoneChanged(notNull())
verify(mockLargeClockView).onTimeZoneChanged(notNull())
verify(mockSmallClockView).refreshTime()
@@ -147,10 +151,14 @@ class DefaultClockProviderTest : SysuiTestCase() {
@Test
fun defaultClock_events_onColorPaletteChanged() {
val clock = provider.createClock(DEFAULT_CLOCK_ID)
- clock.events.onColorPaletteChanged(resources, true, true)
- verify(mockSmallClockView, times(2)).setColors(eq(DOZE_COLOR), anyInt())
- verify(mockLargeClockView, times(2)).setColors(eq(DOZE_COLOR), anyInt())
+ verify(mockSmallClockView).setColors(Color.MAGENTA, Color.MAGENTA)
+ verify(mockLargeClockView).setColors(Color.MAGENTA, Color.MAGENTA)
+
+ clock.events.onColorPaletteChanged(resources)
+
+ verify(mockSmallClockView).setColors(eq(DOZE_COLOR), anyInt())
+ verify(mockLargeClockView).setColors(eq(DOZE_COLOR), anyInt())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
index b719c7f9e54e..a6381d13f7da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
@@ -32,7 +32,6 @@ import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Assert;
@@ -58,8 +57,6 @@ public class DynamicPrivacyControllerTest extends SysuiTestCase {
mDynamicPrivacyController = new DynamicPrivacyController(
mLockScreenUserManager, mKeyguardStateController,
mock(StatusBarStateController.class));
- mDynamicPrivacyController.setStatusBarKeyguardViewManager(
- mock(StatusBarKeyguardViewManager.class));
mDynamicPrivacyController.addListener(mListener);
// Disable dynamic privacy by default
allowNotificationsInPublic(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 365e60868b42..4dea6be9d2ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -95,8 +95,6 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
@Mock
private AuthController mAuthController;
@Mock
- private DozeParameters mDozeParameters;
- @Mock
private MetricsLogger mMetricsLogger;
@Mock
private NotificationMediaManager mNotificationMediaManager;
@@ -120,7 +118,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
TestableResources res = getContext().getOrCreateTestableResources();
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
when(mKeyguardStateController.isUnlocked()).thenReturn(false);
@@ -132,7 +130,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mBiometricUnlockController = new BiometricUnlockController(mDozeScrimController,
mKeyguardViewMediator, mScrimController,
mNotificationShadeWindowController, mKeyguardStateController, mHandler,
- mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters,
+ mUpdateMonitor, res.getResources(), mKeyguardBypassController,
mMetricsLogger, mDumpManager, mPowerManager,
mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
mAuthController, mStatusBarStateController,
@@ -170,7 +168,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
public void onBiometricAuthenticated_whenFingerprintAndNotInteractive_wakeAndUnlock() {
reset(mUpdateMonitor);
reset(mStatusBarKeyguardViewManager);
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mDozeScrimController.isPulsing()).thenReturn(true);
// the value of isStrongBiometric doesn't matter here since we only care about the returned
@@ -187,7 +185,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
public void onBiometricAuthenticated_whenDeviceIsAlreadyUnlocked_wakeAndUnlock() {
reset(mUpdateMonitor);
reset(mStatusBarKeyguardViewManager);
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
when(mKeyguardStateController.isUnlocked()).thenReturn(true);
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mDozeScrimController.isPulsing()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 05f87601b1ff..ad497a2ec1e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -516,32 +516,32 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void executeRunnableDismissingKeyguard_nullRunnable_showingAndOccluded() {
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
- when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(true);
mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, false, false, false);
}
@Test
public void executeRunnableDismissingKeyguard_nullRunnable_showing() {
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
- when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, false, false, false);
}
@Test
public void executeRunnableDismissingKeyguard_nullRunnable_notShowing() {
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
- when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, false, false, false);
}
@Test
public void executeRunnableDismissingKeyguard_dreaming_notShowing() throws RemoteException {
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
- when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
when(mKeyguardUpdateMonitor.isDreaming()).thenReturn(true);
mCentralSurfaces.executeRunnableDismissingKeyguard(() -> {},
@@ -555,8 +555,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void executeRunnableDismissingKeyguard_notDreaming_notShowing() throws RemoteException {
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
- when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
when(mKeyguardUpdateMonitor.isDreaming()).thenReturn(false);
mCentralSurfaces.executeRunnableDismissingKeyguard(() -> {},
@@ -571,10 +571,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void lockscreenStateMetrics_notShowing() {
// uninteresting state, except that fingerprint must be non-zero
- when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
// interesting state
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardStateController.isMethodSecure()).thenReturn(false);
mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
@@ -589,10 +589,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void lockscreenStateMetrics_notShowing_secure() {
// uninteresting state, except that fingerprint must be non-zero
- when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
// interesting state
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
@@ -608,10 +608,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void lockscreenStateMetrics_isShowing() {
// uninteresting state, except that fingerprint must be non-zero
- when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
// interesting state
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardStateController.isMethodSecure()).thenReturn(false);
@@ -627,10 +627,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void lockscreenStateMetrics_isShowing_secure() {
// uninteresting state, except that fingerprint must be non-zero
- when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
// interesting state
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
@@ -646,10 +646,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void lockscreenStateMetrics_isShowingBouncer() {
// uninteresting state, except that fingerprint must be non-zero
- when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
// interesting state
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
@@ -1053,9 +1053,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
@Test
- public void startActivityDismissingKeyguard_isShowingandIsOccluded() {
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
- when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(true);
+ public void startActivityDismissingKeyguard_isShowingAndIsOccluded() {
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(true);
mCentralSurfaces.startActivityDismissingKeyguard(
new Intent(),
/* onlyProvisioned = */false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
new file mode 100644
index 000000000000..a986777afa22
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022 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.phone;
+
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+/**
+ * Mock implementation of KeyguardStateController which tracks showing and occluded states
+ * based on {@link #notifyKeyguardState(boolean showing, boolean occluded)}}.
+ */
+public class FakeKeyguardStateController implements KeyguardStateController {
+ private boolean mShowing;
+ private boolean mOccluded;
+ private boolean mCanDismissLockScreen;
+
+ @Override
+ public void notifyKeyguardState(boolean showing, boolean occluded) {
+ mShowing = showing;
+ mOccluded = occluded;
+ }
+
+ @Override
+ public boolean isShowing() {
+ return mShowing;
+ }
+
+ @Override
+ public boolean isOccluded() {
+ return mOccluded;
+ }
+
+ public void setCanDismissLockScreen(boolean canDismissLockScreen) {
+ mCanDismissLockScreen = canDismissLockScreen;
+ }
+
+ @Override
+ public boolean canDismissLockScreen() {
+ return mCanDismissLockScreen;
+ }
+
+ @Override
+ public boolean isBouncerShowing() {
+ return false;
+ }
+
+ @Override
+ public boolean isKeyguardScreenRotationAllowed() {
+ return false;
+ }
+
+ @Override
+ public boolean isMethodSecure() {
+ return true;
+ }
+
+ @Override
+ public boolean isTrusted() {
+ return false;
+ }
+
+ @Override
+ public boolean isKeyguardGoingAway() {
+ return false;
+ }
+
+ @Override
+ public boolean isKeyguardFadingAway() {
+ return false;
+ }
+
+ @Override
+ public boolean isLaunchTransitionFadingAway() {
+ return false;
+ }
+
+ @Override
+ public long getKeyguardFadingAwayDuration() {
+ return 0;
+ }
+
+ @Override
+ public long getKeyguardFadingAwayDelay() {
+ return 0;
+ }
+
+ @Override
+ public long calculateGoingToFullShadeDelay() {
+ return 0;
+ }
+
+ @Override
+ public float getDismissAmount() {
+ return 0f;
+ }
+
+ @Override
+ public boolean isDismissingFromSwipe() {
+ return false;
+ }
+
+ @Override
+ public boolean isFlingingToDismissKeyguard() {
+ return false;
+ }
+
+ @Override
+ public boolean isFlingingToDismissKeyguardDuringSwipeGesture() {
+ return false;
+ }
+
+ @Override
+ public boolean isSnappingKeyguardBackAfterSwipe() {
+ return false;
+ }
+
+ @Override
+ public void notifyPanelFlingStart(boolean dismiss) {
+ }
+
+ @Override
+ public void notifyPanelFlingEnd() {
+ }
+
+ @Override
+ public void addCallback(Callback listener) {
+ }
+
+ @Override
+ public void removeCallback(Callback listener) {
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 8c18f58ca10d..8da8d049516e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -35,6 +36,10 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewRootImpl;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
+import android.window.WindowOnBackInvokedDispatcher;
import androidx.test.filters.SmallTest;
@@ -64,7 +69,6 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.google.common.truth.Truth;
@@ -73,6 +77,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -89,7 +94,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock private ViewMediatorCallback mViewMediatorCallback;
@Mock private LockPatternUtils mLockPatternUtils;
- @Mock private KeyguardStateController mKeyguardStateController;
@Mock private CentralSurfaces mCentralSurfaces;
@Mock private ViewGroup mContainer;
@Mock private NotificationPanelViewController mNotificationPanelView;
@@ -118,6 +122,14 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
+ private FakeKeyguardStateController mKeyguardStateController =
+ spy(new FakeKeyguardStateController());
+
+ @Mock private ViewRootImpl mViewRootImpl;
+ @Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
+ @Captor
+ private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
+
@Before
public void setUp() {
@@ -154,7 +166,14 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mFeatureFlags,
mBouncerCallbackInteractor,
mBouncerInteractor,
- mBouncerView);
+ mBouncerView) {
+ @Override
+ public ViewRootImpl getViewRootImpl() {
+ return mViewRootImpl;
+ }
+ };
+ when(mViewRootImpl.getOnBackInvokedDispatcher())
+ .thenReturn(mOnBackInvokedDispatcher);
mStatusBarKeyguardViewManager.registerCentralSurfaces(
mCentralSurfaces,
mNotificationPanelView,
@@ -162,7 +181,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mBiometricUnlockController,
mNotificationContainer,
mBypassController);
- when(mKeyguardStateController.isOccluded()).thenReturn(false);
mStatusBarKeyguardViewManager.show(null);
ArgumentCaptor<KeyguardBouncer.BouncerExpansionCallback> callbackArgumentCaptor =
ArgumentCaptor.forClass(KeyguardBouncer.BouncerExpansionCallback.class);
@@ -235,7 +253,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void onPanelExpansionChanged_showsBouncerWhenSwiping() {
- when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+ mKeyguardStateController.setCanDismissLockScreen(false);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
verify(mBouncer).show(eq(false), eq(false));
@@ -322,13 +340,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
- public void setOccluded_onKeyguardOccludedChangedCalledCorrectly() {
+ public void setOccluded_onKeyguardOccludedChangedCalled() {
clearInvocations(mKeyguardStateController);
clearInvocations(mKeyguardUpdateMonitor);
- // Should be false to start, so no invocations
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, false /* animated */);
- verify(mKeyguardStateController, never()).notifyKeyguardState(anyBoolean(), anyBoolean());
+ verify(mKeyguardStateController).notifyKeyguardState(true, false);
clearInvocations(mKeyguardUpdateMonitor);
clearInvocations(mKeyguardStateController);
@@ -339,8 +356,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
clearInvocations(mKeyguardUpdateMonitor);
clearInvocations(mKeyguardStateController);
- mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
- verify(mKeyguardStateController, never()).notifyKeyguardState(anyBoolean(), anyBoolean());
+ mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, false /* animated */);
+ verify(mKeyguardStateController).notifyKeyguardState(true, false);
}
@Test
@@ -408,7 +425,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true);
assertTrue(
"Is showing not accurate when alternative auth showing",
- mStatusBarKeyguardViewManager.isShowing());
+ mStatusBarKeyguardViewManager.isBouncerShowing());
}
@Test
@@ -509,6 +526,37 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
+ public void testPredictiveBackCallback_registration() {
+ /* verify that a predictive back callback is registered when the bouncer becomes visible */
+ mBouncerExpansionCallback.onVisibilityChanged(true);
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
+ mOnBackInvokedCallback.capture());
+
+ /* verify that the same callback is unregistered when the bouncer becomes invisible */
+ mBouncerExpansionCallback.onVisibilityChanged(false);
+ verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(
+ eq(mOnBackInvokedCallback.getValue()));
+ }
+
+ @Test
+ public void testPredictiveBackCallback_invocationHidesBouncer() {
+ mBouncerExpansionCallback.onVisibilityChanged(true);
+ /* capture the predictive back callback during registration */
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
+ mOnBackInvokedCallback.capture());
+
+ when(mBouncer.isShowing()).thenReturn(true);
+ when(mCentralSurfaces.shouldKeyguardHideImmediately()).thenReturn(true);
+ /* invoke the back callback directly */
+ mOnBackInvokedCallback.getValue().onBackInvoked();
+
+ /* verify that the bouncer will be hidden as a result of the invocation */
+ verify(mCentralSurfaces).setBouncerShowing(eq(false));
+ }
+
+ @Test
public void testReportBouncerOnDreamWhenVisible() {
mBouncerExpansionCallback.onVisibilityChanged(true);
verify(mCentralSurfaces).setBouncerShowingOverDream(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 7cb28068fe6c..c4abedd0eed4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -17,7 +17,9 @@
package com.android.systemui.temporarydisplay
import android.content.Context
+import android.graphics.Rect
import android.os.PowerManager
+import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
@@ -258,14 +260,20 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
var mostRecentViewInfo: ViewInfo? = null
override val windowLayoutParams = commonWindowLayoutParams
+
+ override fun start() {}
+
override fun updateView(newInfo: ViewInfo, currentView: ViewGroup) {
- super.updateView(newInfo, currentView)
mostRecentViewInfo = newInfo
}
- override fun shouldIgnoreViewRemoval(removalReason: String): Boolean {
+ override fun shouldIgnoreViewRemoval(info: ViewInfo, removalReason: String): Boolean {
return shouldIgnoreViewRemoval
}
+
+ override fun getTouchableRegion(view: View, outRect: Rect) {
+ outRect.setEmpty()
+ }
}
inner class ViewInfo(val name: String) : TemporaryViewInfo {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt
new file mode 100644
index 000000000000..7586fe48b308
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 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.temporarydisplay
+
+import android.graphics.Rect
+import android.view.View
+import android.view.ViewTreeObserver
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class TouchableRegionViewControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var view: View
+ @Mock private lateinit var viewTreeObserver: ViewTreeObserver
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(view.viewTreeObserver).thenReturn(viewTreeObserver)
+ }
+
+ @Test
+ fun viewAttached_listenerAdded() {
+ val controller = TouchableRegionViewController(view) { _, _ -> }
+
+ controller.onViewAttached()
+
+ verify(viewTreeObserver).addOnComputeInternalInsetsListener(any())
+ }
+
+ @Test
+ fun viewDetached_listenerRemoved() {
+ val controller = TouchableRegionViewController(view) { _, _ -> }
+
+ controller.onViewDetached()
+
+ verify(viewTreeObserver).removeOnComputeInternalInsetsListener(any())
+ }
+
+ @Test
+ fun listener_usesPassedInFunction() {
+ val controller =
+ TouchableRegionViewController(view) { _, outRect -> outRect.set(1, 2, 3, 4) }
+
+ controller.onViewAttached()
+
+ val captor =
+ ArgumentCaptor.forClass(ViewTreeObserver.OnComputeInternalInsetsListener::class.java)
+ verify(viewTreeObserver).addOnComputeInternalInsetsListener(captor.capture())
+ val listener = captor.value!!
+
+ val inoutInfo = ViewTreeObserver.InternalInsetsInfo()
+ listener.onComputeInternalInsets(inoutInfo)
+
+ assertThat(inoutInfo.touchableRegion.bounds).isEqualTo(Rect(1, 2, 3, 4))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 50259b5246f5..2a93ffff87a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -885,4 +885,31 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
.isEqualTo(new OverlayIdentifier("ff00ff00"));
}
+
+ // Regression test for b/234603929, where a reboot would generate a wallpaper colors changed
+ // event for the already-set colors that would then set the theme incorrectly on screen sleep.
+ @Test
+ public void onWallpaperColorsSetToSame_keepsTheme() {
+ // Set initial colors and verify.
+ WallpaperColors startingColors = new WallpaperColors(Color.valueOf(Color.RED),
+ Color.valueOf(Color.BLUE), null);
+ WallpaperColors sameColors = new WallpaperColors(Color.valueOf(Color.RED),
+ Color.valueOf(Color.BLUE), null);
+ mColorsListener.getValue().onColorsChanged(startingColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
+ verify(mThemeOverlayApplier)
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ clearInvocations(mThemeOverlayApplier);
+
+ // Set to the same colors.
+ mColorsListener.getValue().onColorsChanged(sameColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
+ verify(mThemeOverlayApplier, never())
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+
+ // Verify that no change resulted.
+ mWakefulnessLifecycleObserver.getValue().onFinishedGoingToSleep();
+ verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
+ any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt
index 4a8e0552d778..d951f366c595 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt
@@ -120,6 +120,27 @@ class UserRepositoryImplRefactoredTest : UserRepositoryImplTest() {
assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedNonGuestUserId)
}
+ @Test
+ fun `refreshUsers - sorts by creation time`() = runSelfCancelingTest {
+ underTest = create(this)
+ val unsortedUsers =
+ setUpUsers(
+ count = 3,
+ selectedIndex = 0,
+ )
+ unsortedUsers[0].creationTime = 900
+ unsortedUsers[1].creationTime = 700
+ unsortedUsers[2].creationTime = 999
+ val expectedUsers = listOf(unsortedUsers[1], unsortedUsers[0], unsortedUsers[2])
+ var userInfos: List<UserInfo>? = null
+ var selectedUserInfo: UserInfo? = null
+ underTest.userInfos.onEach { userInfos = it }.launchIn(this)
+ underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
+
+ underTest.refreshUsers()
+ assertThat(userInfos).isEqualTo(expectedUsers)
+ }
+
private fun setUpUsers(
count: Int,
hasGuest: Boolean = false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorRefactoredTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorRefactoredTest.kt
index 3d5695a09ebc..37c378c9a530 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorRefactoredTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorRefactoredTest.kt
@@ -47,7 +47,9 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@SmallTest
@@ -73,6 +75,66 @@ class UserInteractorRefactoredTest : UserInteractorTest() {
}
@Test
+ fun `onRecordSelected - user`() =
+ runBlocking(IMMEDIATE) {
+ val userInfos = createUserInfos(count = 3, includeGuest = false)
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
+
+ underTest.onRecordSelected(UserRecord(info = userInfos[1]), dialogShower)
+
+ verify(dialogShower).dismiss()
+ verify(activityManager).switchUser(userInfos[1].id)
+ Unit
+ }
+
+ @Test
+ fun `onRecordSelected - switch to guest user`() =
+ runBlocking(IMMEDIATE) {
+ val userInfos = createUserInfos(count = 3, includeGuest = true)
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
+
+ underTest.onRecordSelected(UserRecord(info = userInfos.last()))
+
+ verify(activityManager).switchUser(userInfos.last().id)
+ Unit
+ }
+
+ @Test
+ fun `onRecordSelected - enter guest mode`() =
+ runBlocking(IMMEDIATE) {
+ val userInfos = createUserInfos(count = 3, includeGuest = false)
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
+ val guestUserInfo = createUserInfo(id = 1337, name = "guest", isGuest = true)
+ whenever(manager.createGuest(any())).thenReturn(guestUserInfo)
+
+ underTest.onRecordSelected(UserRecord(isGuest = true), dialogShower)
+
+ verify(dialogShower).dismiss()
+ verify(manager).createGuest(any())
+ Unit
+ }
+
+ @Test
+ fun `onRecordSelected - action`() =
+ runBlocking(IMMEDIATE) {
+ val userInfos = createUserInfos(count = 3, includeGuest = true)
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
+
+ underTest.onRecordSelected(UserRecord(isAddSupervisedUser = true), dialogShower)
+
+ verify(dialogShower, never()).dismiss()
+ verify(activityStarter).startActivity(any(), anyBoolean())
+ }
+
+ @Test
fun `users - switcher enabled`() =
runBlocking(IMMEDIATE) {
val userInfos = createUserInfos(count = 3, includeGuest = true)
@@ -336,10 +398,14 @@ class UserInteractorRefactoredTest : UserInteractorTest() {
var dialogRequest: ShowDialogRequestModel? = null
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
- underTest.selectUser(newlySelectedUserId = guestUserInfo.id)
+ underTest.selectUser(
+ newlySelectedUserId = guestUserInfo.id,
+ dialogShower = dialogShower,
+ )
assertThat(dialogRequest)
.isInstanceOf(ShowDialogRequestModel.ShowExitGuestDialog::class.java)
+ verify(dialogShower, never()).dismiss()
job.cancel()
}
@@ -355,10 +421,11 @@ class UserInteractorRefactoredTest : UserInteractorTest() {
var dialogRequest: ShowDialogRequestModel? = null
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
- underTest.selectUser(newlySelectedUserId = userInfos[0].id)
+ underTest.selectUser(newlySelectedUserId = userInfos[0].id, dialogShower = dialogShower)
assertThat(dialogRequest)
.isInstanceOf(ShowDialogRequestModel.ShowExitGuestDialog::class.java)
+ verify(dialogShower, never()).dismiss()
job.cancel()
}
@@ -372,10 +439,11 @@ class UserInteractorRefactoredTest : UserInteractorTest() {
var dialogRequest: ShowDialogRequestModel? = null
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
- underTest.selectUser(newlySelectedUserId = userInfos[1].id)
+ underTest.selectUser(newlySelectedUserId = userInfos[1].id, dialogShower = dialogShower)
assertThat(dialogRequest).isNull()
verify(activityManager).switchUser(userInfos[1].id)
+ verify(dialogShower).dismiss()
job.cancel()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 8465f4f46d62..1680c36cef87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -27,6 +27,7 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.UserSwitcherController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
@@ -46,6 +47,7 @@ abstract class UserInteractorTest : SysuiTestCase() {
@Mock protected lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock protected lateinit var devicePolicyManager: DevicePolicyManager
@Mock protected lateinit var uiEventLogger: UiEventLogger
+ @Mock protected lateinit var dialogShower: UserSwitchDialogController.DialogShower
protected lateinit var underTest: UserInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
index 125b3627b342..17d81c8338cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
@@ -73,10 +73,16 @@ public class ConditionMonitorTest extends SysuiTestCase {
.addConditions(mConditions);
}
+ private Condition createMockCondition() {
+ final Condition condition = Mockito.mock(Condition.class);
+ when(condition.isConditionSet()).thenReturn(true);
+ return condition;
+ }
+
@Test
public void testOverridingCondition() {
- final Condition overridingCondition = Mockito.mock(Condition.class);
- final Condition regularCondition = Mockito.mock(Condition.class);
+ final Condition overridingCondition = createMockCondition();
+ final Condition regularCondition = createMockCondition();
final Monitor.Callback callback = Mockito.mock(Monitor.Callback.class);
final Monitor.Callback referenceCallback = Mockito.mock(Monitor.Callback.class);
@@ -127,9 +133,9 @@ public class ConditionMonitorTest extends SysuiTestCase {
*/
@Test
public void testMultipleOverridingConditions() {
- final Condition overridingCondition = Mockito.mock(Condition.class);
- final Condition overridingCondition2 = Mockito.mock(Condition.class);
- final Condition regularCondition = Mockito.mock(Condition.class);
+ final Condition overridingCondition = createMockCondition();
+ final Condition overridingCondition2 = createMockCondition();
+ final Condition regularCondition = createMockCondition();
final Monitor.Callback callback = Mockito.mock(Monitor.Callback.class);
final Monitor monitor = new Monitor(mExecutor);
@@ -340,4 +346,114 @@ public class ConditionMonitorTest extends SysuiTestCase {
mExecutor.runAllReady();
verify(callback).onConditionsChanged(true);
}
+
+ @Test
+ public void clearCondition_shouldUpdateValue() {
+ mCondition1.fakeUpdateCondition(false);
+ mCondition2.fakeUpdateCondition(true);
+ mCondition3.fakeUpdateCondition(true);
+
+ final Monitor.Callback callback =
+ mock(Monitor.Callback.class);
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
+ mExecutor.runAllReady();
+ verify(callback).onConditionsChanged(false);
+
+ mCondition1.clearCondition();
+ mExecutor.runAllReady();
+ verify(callback).onConditionsChanged(true);
+ }
+
+ @Test
+ public void unsetCondition_shouldNotAffectValue() {
+ final FakeCondition settableCondition = new FakeCondition(null, false);
+ mCondition1.fakeUpdateCondition(true);
+ mCondition2.fakeUpdateCondition(true);
+ mCondition3.fakeUpdateCondition(true);
+
+ final Monitor.Callback callback =
+ mock(Monitor.Callback.class);
+
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback)
+ .addCondition(settableCondition)
+ .build());
+
+ mExecutor.runAllReady();
+ verify(callback).onConditionsChanged(true);
+ }
+
+ @Test
+ public void setUnsetCondition_shouldAffectValue() {
+ final FakeCondition settableCondition = new FakeCondition(null, false);
+ mCondition1.fakeUpdateCondition(true);
+ mCondition2.fakeUpdateCondition(true);
+ mCondition3.fakeUpdateCondition(true);
+
+ final Monitor.Callback callback =
+ mock(Monitor.Callback.class);
+
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback)
+ .addCondition(settableCondition)
+ .build());
+
+ mExecutor.runAllReady();
+ verify(callback).onConditionsChanged(true);
+ clearInvocations(callback);
+
+ settableCondition.fakeUpdateCondition(false);
+ mExecutor.runAllReady();
+ verify(callback).onConditionsChanged(false);
+ clearInvocations(callback);
+
+
+ settableCondition.clearCondition();
+ mExecutor.runAllReady();
+ verify(callback).onConditionsChanged(true);
+ }
+
+ @Test
+ public void clearingOverridingCondition_shouldBeExcluded() {
+ final FakeCondition overridingCondition = new FakeCondition(true, true);
+ mCondition1.fakeUpdateCondition(false);
+ mCondition2.fakeUpdateCondition(false);
+ mCondition3.fakeUpdateCondition(false);
+
+ final Monitor.Callback callback =
+ mock(Monitor.Callback.class);
+
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback)
+ .addCondition(overridingCondition)
+ .build());
+
+ mExecutor.runAllReady();
+ verify(callback).onConditionsChanged(true);
+ clearInvocations(callback);
+
+ overridingCondition.clearCondition();
+ mExecutor.runAllReady();
+ verify(callback).onConditionsChanged(false);
+ }
+
+ @Test
+ public void settingUnsetOverridingCondition_shouldBeIncluded() {
+ final FakeCondition overridingCondition = new FakeCondition(null, true);
+ mCondition1.fakeUpdateCondition(false);
+ mCondition2.fakeUpdateCondition(false);
+ mCondition3.fakeUpdateCondition(false);
+
+ final Monitor.Callback callback =
+ mock(Monitor.Callback.class);
+
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback)
+ .addCondition(overridingCondition)
+ .build());
+
+ mExecutor.runAllReady();
+ verify(callback).onConditionsChanged(false);
+ clearInvocations(callback);
+
+ overridingCondition.fakeUpdateCondition(true);
+ mExecutor.runAllReady();
+ verify(callback).onConditionsChanged(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java
index 9e0f863acc1a..0b53133e9353 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java
@@ -133,4 +133,12 @@ public class ConditionTest extends SysuiTestCase {
mCondition.fakeUpdateCondition(false);
verify(callback, never()).onConditionChanged(eq(mCondition));
}
+
+ @Test
+ public void clearCondition_reportsNotSet() {
+ mCondition.fakeUpdateCondition(false);
+ assertThat(mCondition.isConditionSet()).isTrue();
+ mCondition.clearCondition();
+ assertThat(mCondition.isConditionSet()).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
index dead1592992d..e3cd9b2d6eaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
@@ -1,12 +1,31 @@
+/*
+ * Copyright (C) 2022 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.util.view
+import android.graphics.Rect
import android.view.View
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.spy
import org.mockito.Mockito.`when`
@@ -25,6 +44,12 @@ class ViewUtilTest : SysuiTestCase() {
location[0] = VIEW_LEFT
location[1] = VIEW_TOP
`when`(view.locationOnScreen).thenReturn(location)
+ doAnswer { invocation ->
+ val pos = invocation.arguments[0] as IntArray
+ pos[0] = VIEW_LEFT
+ pos[1] = VIEW_TOP
+ null
+ }.`when`(view).getLocationInWindow(any())
}
@Test
@@ -64,6 +89,18 @@ class ViewUtilTest : SysuiTestCase() {
fun touchIsWithinView_yTooLarge_returnsFalse() {
assertThat(viewUtil.touchIsWithinView(view, VIEW_LEFT + 1f, VIEW_BOTTOM + 1f)).isFalse()
}
+
+ @Test
+ fun setRectToViewWindowLocation_rectHasLocation() {
+ val outRect = Rect()
+
+ viewUtil.setRectToViewWindowLocation(view, outRect)
+
+ assertThat(outRect.left).isEqualTo(VIEW_LEFT)
+ assertThat(outRect.right).isEqualTo(VIEW_RIGHT)
+ assertThat(outRect.top).isEqualTo(VIEW_TOP)
+ assertThat(outRect.bottom).isEqualTo(VIEW_BOTTOM)
+ }
}
private const val VIEW_LEFT = 30
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index c56fdb17b5f1..5d52be2675e3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -16,6 +16,8 @@
package com.android.systemui.flags
+import java.io.PrintWriter
+
class FakeFeatureFlags : FeatureFlags {
private val booleanFlags = mutableMapOf<Int, Boolean>()
private val stringFlags = mutableMapOf<Int, String>()
@@ -106,6 +108,10 @@ class FakeFeatureFlags : FeatureFlags {
}
}
+ override fun dump(writer: PrintWriter, args: Array<out String>?) {
+ // no-op
+ }
+
private fun flagName(flagId: Int): String {
return knownFlagNames[flagId] ?: "UNKNOWN(id=$flagId)"
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/condition/FakeCondition.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/condition/FakeCondition.java
index 9d5ccbec87ea..1353ad25d057 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/condition/FakeCondition.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/condition/FakeCondition.java
@@ -21,6 +21,14 @@ package com.android.systemui.util.condition;
* condition fulfillment.
*/
public class FakeCondition extends Condition {
+ FakeCondition() {
+ super();
+ }
+
+ FakeCondition(Boolean initialValue, Boolean overriding) {
+ super(initialValue, overriding);
+ }
+
@Override
public void start() {}
diff --git a/services/api/current.txt b/services/api/current.txt
index 5f203df52f39..42ae10ef8d5e 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -48,10 +48,82 @@ package com.android.server.pm {
public interface PackageManagerLocal {
method public void reconcileSdkData(@Nullable String, @NonNull String, @NonNull java.util.List<java.lang.String>, int, int, int, @NonNull String, int) throws java.io.IOException;
+ method @NonNull public com.android.server.pm.PackageManagerLocal.FilteredSnapshot withFilteredSnapshot();
+ method @NonNull public com.android.server.pm.PackageManagerLocal.FilteredSnapshot withFilteredSnapshot(int, @NonNull android.os.UserHandle);
+ method @NonNull public com.android.server.pm.PackageManagerLocal.UnfilteredSnapshot withUnfilteredSnapshot();
field public static final int FLAG_STORAGE_CE = 2; // 0x2
field public static final int FLAG_STORAGE_DE = 1; // 0x1
}
+ public static interface PackageManagerLocal.FilteredSnapshot extends java.lang.AutoCloseable {
+ method public void close();
+ method public void forAllPackageStates(@NonNull java.util.function.Consumer<com.android.server.pm.pkg.PackageState>);
+ method @Nullable public com.android.server.pm.pkg.PackageState getPackageState(@NonNull String);
+ }
+
+ public static interface PackageManagerLocal.UnfilteredSnapshot extends java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public com.android.server.pm.PackageManagerLocal.FilteredSnapshot filtered(int, @NonNull android.os.UserHandle);
+ method @NonNull public java.util.Map<java.lang.String,com.android.server.pm.pkg.PackageState> getPackageStates();
+ }
+
+}
+
+package com.android.server.pm.pkg {
+
+ public interface AndroidPackage {
+ method @NonNull public java.util.List<java.lang.String> getLibraryNames();
+ method @Nullable public String getSdkLibraryName();
+ method @NonNull public java.util.List<com.android.server.pm.pkg.AndroidPackageSplit> getSplits();
+ method @Nullable public String getStaticSharedLibraryName();
+ method public int getTargetSdkVersion();
+ method public boolean isDebuggable();
+ method public boolean isIsolatedSplitLoading();
+ method public boolean isSignedWithPlatformKey();
+ method public boolean isUseEmbeddedDex();
+ method public boolean isUsesNonSdkApi();
+ method public boolean isVmSafeMode();
+ }
+
+ public interface AndroidPackageSplit {
+ method @Nullable public String getClassLoaderName();
+ method @NonNull public java.util.List<com.android.server.pm.pkg.AndroidPackageSplit> getDependencies();
+ method @Nullable public String getName();
+ method @NonNull public String getPath();
+ method public int getRevisionCode();
+ method public boolean isHasCode();
+ }
+
+ public interface PackageState {
+ method @Nullable public com.android.server.pm.pkg.AndroidPackage getAndroidPackage();
+ method public int getAppId();
+ method @NonNull public String getPackageName();
+ method @Nullable public String getPrimaryCpuAbi();
+ method @Nullable public String getSecondaryCpuAbi();
+ method @NonNull public com.android.server.pm.pkg.PackageUserState getStateForUser(@NonNull android.os.UserHandle);
+ method @NonNull public java.util.List<com.android.server.pm.pkg.SharedLibrary> getUsesLibraries();
+ method public boolean isPrivileged();
+ method public boolean isSystem();
+ method public boolean isUpdatedSystemApp();
+ }
+
+ public interface PackageUserState {
+ method public boolean isInstalled();
+ }
+
+ public interface SharedLibrary {
+ method @NonNull public java.util.List<java.lang.String> getAllCodePaths();
+ method @NonNull public android.content.pm.VersionedPackage getDeclaringPackage();
+ method @NonNull public java.util.List<com.android.server.pm.pkg.SharedLibrary> getDependencies();
+ method @NonNull public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
+ method @Nullable public String getName();
+ method @Nullable public String getPackageName();
+ method @Nullable public String getPath();
+ method public int getType();
+ method public long getVersion();
+ method public boolean isNative();
+ }
+
}
package com.android.server.pm.snapshot {
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 0ab5a8afe907..80d9d972ed0a 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -270,7 +270,8 @@ class AssociationRequestsProcessor {
final long callingIdentity = Binder.clearCallingIdentity();
try {
association = mService.createAssociation(userId, packageName, macAddress,
- request.getDisplayName(), request.getDeviceProfile(), request.isSelfManaged());
+ request.getDisplayName(), request.getDeviceProfile(),
+ request.getAssociatedDevice(), request.isSelfManaged());
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 0426c79358ba..f2cb6028e854 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -55,6 +55,7 @@ import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.companion.AssociatedDevice;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.DeviceNotAssociatedException;
@@ -872,23 +873,25 @@ public class CompanionDeviceManagerService extends SystemService {
/**
* @deprecated use
- * {@link #createAssociation(int, String, MacAddress, CharSequence, String, boolean)}
+ * {@link #createAssociation(int, String, MacAddress, CharSequence, String, AssociatedDevice,
+ * boolean)}
*/
@Deprecated
void legacyCreateAssociation(@UserIdInt int userId, @NonNull String deviceMacAddress,
@NonNull String packageName, @Nullable String deviceProfile) {
final MacAddress macAddress = MacAddress.fromString(deviceMacAddress);
- createAssociation(userId, packageName, macAddress, null, deviceProfile, false);
+ createAssociation(userId, packageName, macAddress, null, deviceProfile, null, false);
}
AssociationInfo createAssociation(@UserIdInt int userId, @NonNull String packageName,
@Nullable MacAddress macAddress, @Nullable CharSequence displayName,
- @Nullable String deviceProfile, boolean selfManaged) {
+ @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice,
+ boolean selfManaged) {
final int id = getNewAssociationIdForPackage(userId, packageName);
final long timestamp = System.currentTimeMillis();
final AssociationInfo association = new AssociationInfo(id, userId, packageName,
- macAddress, displayName, deviceProfile, selfManaged,
+ macAddress, displayName, deviceProfile, associatedDevice, selfManaged,
/* notifyOnDeviceNearby */ false, /* revoked */ false, timestamp, Long.MAX_VALUE);
Slog.i(TAG, "New CDM association created=" + association);
mAssociationStore.addAssociation(association);
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 4b56c1b28036..c4f576647058 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -229,6 +229,11 @@ final class PersistentDataStore {
/**
* Reads previously persisted data for the given user "into" the provided containers.
*
+ * Note that {@link AssociationInfo#getAssociatedDevice()} will always be {@code null} after
+ * retrieval from this datastore because it is not persisted (by design). This means that
+ * persisted data is not guaranteed to be identical to the initial data that was stored at the
+ * time of association.
+ *
* @param userId Android UserID
* @param associationsOut a container to read the {@link AssociationInfo}s "into".
* @param previouslyUsedIdsPerPackageOut a container to read the used IDs "into".
@@ -293,6 +298,9 @@ final class PersistentDataStore {
/**
* Persisted data to the disk.
*
+ * Note that associatedDevice field in {@link AssociationInfo} is not persisted by this
+ * datastore implementation.
+ *
* @param userId Android UserID
* @param associations a set of user's associations.
* @param previouslyUsedIdsPerPackage a set previously used Association IDs for the user.
@@ -419,7 +427,7 @@ final class PersistentDataStore {
final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
out.add(new AssociationInfo(associationId, userId, appPackage,
- MacAddress.fromString(deviceAddress), null, profile,
+ MacAddress.fromString(deviceAddress), null, profile, null,
/* managedByCompanionApp */ false, notify, /* revoked */ false, timeApproved,
Long.MAX_VALUE));
}
@@ -556,9 +564,11 @@ final class PersistentDataStore {
boolean notify, boolean revoked, long timeApproved, long lastTimeConnected) {
AssociationInfo associationInfo = null;
try {
+ // We do not persist AssociatedDevice, which means that AssociationInfo retrieved from
+ // datastore is not guaranteed to be identical to the one from initial association.
associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress,
- displayName, profile, selfManaged, notify, revoked, timeApproved,
- lastTimeConnected);
+ displayName, profile, null, selfManaged, notify, revoked,
+ timeApproved, lastTimeConnected);
} catch (Exception e) {
if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e);
}
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 285c4068d84b..41044bfc70ca 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -16,12 +16,18 @@
package android.os;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
import com.android.internal.os.BinderCallsStats;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
import java.util.List;
+
/**
* Battery stats local system service interface. This is used to pass internal data out of
* BatteryStatsImpl, as well as make unchecked calls into BatteryStatsImpl.
@@ -29,6 +35,19 @@ import java.util.List;
* @hide Only for use within Android OS.
*/
public abstract class BatteryStatsInternal {
+
+ public static final int CPU_WAKEUP_SUBSYSTEM_UNKNOWN = -1;
+ public static final int CPU_WAKEUP_SUBSYSTEM_ALARM = 1;
+
+ /** @hide */
+ @IntDef(prefix = {"CPU_WAKEUP_SUBSYSTEM_"}, value = {
+ CPU_WAKEUP_SUBSYSTEM_UNKNOWN,
+ CPU_WAKEUP_SUBSYSTEM_ALARM,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface CpuWakeupSubsystem {
+ }
+
/**
* Returns the wifi interfaces.
*/
@@ -56,9 +75,10 @@ public abstract class BatteryStatsInternal {
/**
* Inform battery stats how many deferred jobs existed when the app got launched and how
* long ago was the last job execution for the app.
- * @param uid the uid of the app.
+ *
+ * @param uid the uid of the app.
* @param numDeferred number of deferred jobs.
- * @param sinceLast how long in millis has it been since a job was run
+ * @param sinceLast how long in millis has it been since a job was run
*/
public abstract void noteJobsDeferred(int uid, int numDeferred, long sinceLast);
@@ -72,4 +92,11 @@ public abstract class BatteryStatsInternal {
* Informs battery stats of native thread IDs of threads taking incoming binder calls.
*/
public abstract void noteBinderThreadNativeIds(int[] binderThreadNativeTids);
+
+ /**
+ * Reports any activity that could potentially have caused the CPU to wake up.
+ * Accepts a timestamp to allow the reporter to report it before or after the event.
+ */
+ public abstract void noteCpuWakingActivity(@CpuWakeupSubsystem int subsystem,
+ long elapsedMillis, @NonNull int... uids);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b34fe69f5a3e..ee13118f3e5c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5285,7 +5285,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
SystemProperties.set("dev.bootcomplete", "1");
- mUserController.sendBootCompleted(
+ mUserController.onBootComplete(
new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode,
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d9d29d650f03..75d1f6897111 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -81,9 +81,11 @@ import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.StatsEvent;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
@@ -104,6 +106,7 @@ import com.android.server.power.stats.BatteryExternalStatsWorker;
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.power.stats.BatteryUsageStatsProvider;
import com.android.server.power.stats.BatteryUsageStatsStore;
+import com.android.server.power.stats.CpuWakeupStats;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import java.io.File;
@@ -120,6 +123,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -142,6 +146,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private final PowerProfile mPowerProfile;
final BatteryStatsImpl mStats;
+ @GuardedBy("mWakeupStats")
+ final CpuWakeupStats mCpuWakeupStats;
private final BatteryUsageStatsStore mBatteryUsageStatsStore;
private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider;
private final Context mContext;
@@ -373,6 +379,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats,
mBatteryUsageStatsStore);
+ mCpuWakeupStats = new CpuWakeupStats(context, R.xml.irq_device_map);
}
public void publish() {
@@ -464,6 +471,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mStats.noteBinderThreadNativeIds(binderThreadNativeTids);
}
}
+
+ @Override
+ public void noteCpuWakingActivity(int subsystem, long elapsedMillis, int... uids) {
+ Objects.requireNonNull(uids);
+ mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids);
+ }
}
@Override
@@ -2251,12 +2264,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub
try {
String reason;
while ((reason = waitWakeup()) != null) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowUptime = SystemClock.uptimeMillis();
// Wait for the completion of pending works if there is any
awaitCompletion();
-
+ mCpuWakeupStats.noteWakeupTimeAndReason(nowElapsed, nowUptime, reason);
synchronized (mStats) {
- mStats.noteWakeupReasonLocked(reason,
- SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
+ mStats.noteWakeupReasonLocked(reason, nowElapsed, nowUptime);
}
}
} catch (RuntimeException e) {
@@ -2312,6 +2326,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
pw.println(" --read-daily: read-load last written daily stats.");
pw.println(" --settings: dump the settings key/values related to batterystats");
pw.println(" --cpu: dump cpu stats for debugging purpose");
+ pw.println(" --wakeups: dump CPU wakeup history and attribution.");
pw.println(" --power-profile: dump the power profile constants");
pw.println(" --usage: write battery usage stats. Optional arguments:");
pw.println(" --proto: output as a binary protobuffer");
@@ -2567,6 +2582,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
dumpUsageStatsToProto(fd, pw, model, proto);
return;
+ } else if ("--wakeups".equals(arg)) {
+ mCpuWakeupStats.dump(new IndentingPrintWriter(pw, " "),
+ SystemClock.elapsedRealtime());
+ return;
} else if ("-a".equals(arg)) {
flags |= BatteryStats.DUMP_VERBOSE;
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
@@ -2697,12 +2716,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub
} else {
if (DBG) Slog.d(TAG, "begin dumpLocked from UID " + Binder.getCallingUid());
awaitCompletion();
+
synchronized (mStats) {
mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
if (writeData) {
mStats.writeAsyncLocked();
}
}
+ pw.println();
+ mCpuWakeupStats.dump(new IndentingPrintWriter(pw, " "), SystemClock.elapsedRealtime());
+
if (DBG) Slog.d(TAG, "end dumpLocked");
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 77300f7cf228..d7a075b3b966 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -579,7 +579,7 @@ public class BroadcastQueueImpl extends BroadcastQueue {
final Object curReceiver = r.receivers.get(curIndex);
FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, r.curApp.uid,
r.callingUid == -1 ? Process.SYSTEM_UID : r.callingUid,
- ActivityManagerService.getShortAction(r.intent.getAction()),
+ r.intent.getAction(),
curReceiver instanceof BroadcastFilter
? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME
: BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
@@ -757,7 +757,7 @@ public class BroadcastQueueImpl extends BroadcastQueue {
FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED,
receiverUid == -1 ? Process.SYSTEM_UID : receiverUid,
callingUid == -1 ? Process.SYSTEM_UID : callingUid,
- ActivityManagerService.getShortAction(intent.getAction()),
+ intent.getAction(),
BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
dispatchDelay, receiveDelay, 0 /* finish_delay */);
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index fe8d84feb557..89a0283a4264 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1298,7 +1298,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
// Report statistics for each individual receiver
final int uid = getReceiverUid(receiver);
final int senderUid = (r.callingUid == -1) ? Process.SYSTEM_UID : r.callingUid;
- final String actionName = ActivityManagerService.getShortAction(r.intent.getAction());
+ final String actionName = r.intent.getAction();
final int receiverType = (receiver instanceof BroadcastFilter)
? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME
: BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 226c63862226..216a48ec699c 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -439,11 +439,6 @@ class UserController implements Handler.Callback {
mUserLru.add(UserHandle.USER_SYSTEM);
mLockPatternUtils = mInjector.getLockPatternUtils();
updateStartedUserArrayLU();
-
- // TODO(b/232452368): currently mAllowUserUnlocking is only used on devices with HSUM
- // (Headless System User Mode), but on master it will be used by all devices (and hence this
- // initial assignment should be removed).
- mAllowUserUnlocking = !UserManager.isHeadlessSystemUserMode();
}
void setInitialConfig(boolean userSwitchUiEnabled, int maxRunningUsers,
@@ -602,8 +597,11 @@ class UserController implements Handler.Callback {
if (!mInjector.getUserManager().isPreCreated(userId)) {
mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
userId, 0));
- // In case of headless system user mode, do not send boot complete broadcast for
- // system user as it is sent by sendBootCompleted call.
+ // The "locked boot complete" broadcast for the system user is supposed be sent when
+ // the device has finished booting. Normally, that is the same time that the system
+ // user transitions to RUNNING_LOCKED. However, in "headless system user mode", the
+ // system user is explicitly started before the device has finished booting. In
+ // that case, we need to wait until onBootComplete() to send the broadcast.
if (!(UserManager.isHeadlessSystemUserMode() && uss.mHandle.isSystem())) {
// ACTION_LOCKED_BOOT_COMPLETED
sendLockedBootCompletedBroadcast(resultTo, userId);
@@ -1808,15 +1806,13 @@ class UserController implements Handler.Callback {
*/
private boolean maybeUnlockUser(@UserIdInt int userId, @Nullable IProgressListener listener) {
- // Delay user unlocking for headless system user mode until the system boot
- // completes. When the system boot completes, the {@link #onBootCompleted()}
- // method unlocks all started users for headless system user mode. This is done
- // to prevent unlocking the users too early during the system boot up.
- // Otherwise, emulated volumes are mounted too early during the system
- // boot up. When vold is reset on boot complete, vold kills all apps/services
- // (that use these emulated volumes) before unmounting the volumes(b/241929666).
- // In the past, these killings have caused the system to become too unstable on
- // some occasions.
+ // We cannot allow users to be unlocked before PHASE_BOOT_COMPLETED, for two reasons.
+ // First, emulated volumes aren't supposed to be used until then; StorageManagerService
+ // assumes it can reset everything upon reaching PHASE_BOOT_COMPLETED. Second, on some
+ // devices the Weaver HAL needed to unlock the user's storage isn't available until sometime
+ // shortly before PHASE_BOOT_COMPLETED. The below logic enforces a consistent flow across
+ // all devices, regardless of their Weaver implementation.
+ //
// Any unlocks that get delayed by this will be done by onBootComplete() instead.
if (!mAllowUserUnlocking) {
Slogf.i(TAG, "Not unlocking user %d yet because boot hasn't completed", userId);
@@ -2424,11 +2420,8 @@ class UserController implements Handler.Callback {
}
}
- /**
- * @deprecated TODO(b/232452368): this logic will be merged into sendBootCompleted
- */
- @Deprecated
- private void onBootCompletedOnHeadlessSystemUserModeDevices() {
+ void onBootComplete(IIntentReceiver resultTo) {
+ // Now that PHASE_BOOT_COMPLETED has been reached, user unlocking is allowed.
setAllowUserUnlocking(true);
// Get a copy of mStartedUsers to use outside of lock.
@@ -2436,37 +2429,30 @@ class UserController implements Handler.Callback {
synchronized (mLock) {
startedUsers = mStartedUsers.clone();
}
+ // In non-headless system user mode, call finishUserBoot() to transition the system user
+ // from the BOOTING state to RUNNING_LOCKED, then to RUNNING_UNLOCKED if possible.
+ //
+ // In headless system user mode, additional users may have been started, and all users
+ // (including the system user) that ever get started are started explicitly. In this case,
+ // we should *not* transition users out of the BOOTING state using finishUserBoot(), as that
+ // doesn't handle issuing the needed onUserStarting() call, and it would just race with an
+ // explicit start anyway. We do, however, need to send the "locked boot complete" broadcast
+ // for the system user, as that got skipped earlier due to the *device* boot not being
+ // complete yet. We also need to try to unlock all started users, since until now explicit
+ // user starts didn't proceed to unlocking, due to it being too early in the device boot.
+ //
// USER_SYSTEM must be processed first. It will be first in the array, as its ID is lowest.
Preconditions.checkArgument(startedUsers.keyAt(0) == UserHandle.USER_SYSTEM);
for (int i = 0; i < startedUsers.size(); i++) {
- UserState uss = startedUsers.valueAt(i);
- int userId = uss.mHandle.getIdentifier();
- Slogf.i(TAG, "Attempting to unlock user %d on boot complete", userId);
- maybeUnlockUser(userId);
- }
- }
-
- void sendBootCompleted(IIntentReceiver resultTo) {
- if (UserManager.isHeadlessSystemUserMode()) {
- // Unlocking users is delayed until boot complete for headless system user mode.
- onBootCompletedOnHeadlessSystemUserModeDevices();
- }
-
- // Get a copy of mStartedUsers to use outside of lock
- SparseArray<UserState> startedUsers;
- synchronized (mLock) {
- startedUsers = mStartedUsers.clone();
- }
- for (int i = 0; i < startedUsers.size(); i++) {
+ int userId = startedUsers.keyAt(i);
UserState uss = startedUsers.valueAt(i);
if (!UserManager.isHeadlessSystemUserMode()) {
finishUserBoot(uss, resultTo);
- } else if (uss.mHandle.isSystem()) {
- // In case of headless system user mode, send only locked boot complete broadcast
- // for system user since finishUserBoot call will be made using other code path;
- // for non-system user, do nothing since finishUserBoot will be called elsewhere.
- sendLockedBootCompletedBroadcast(resultTo, uss.mHandle.getIdentifier());
- return;
+ } else {
+ if (userId == UserHandle.USER_SYSTEM) {
+ sendLockedBootCompletedBroadcast(resultTo, userId);
+ }
+ maybeUnlockUser(userId);
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 730c4108220a..c59ee83aa895 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -54,6 +54,7 @@ import android.util.Log;
import android.util.PrintWriterPrinter;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -343,7 +344,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "setCommunicationRouteForClient: device: " + device);
}
- AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
"setCommunicationRouteForClient for pid: " + pid
+ " device: " + device
+ " from API: " + eventSource)).printLog(TAG));
@@ -1229,7 +1230,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
private void onSendBecomingNoisyIntent() {
- AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
"broadcast ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG));
mSystemServer.sendDeviceBecomingNoisyIntent();
}
@@ -1467,7 +1468,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
case MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT: {
final BtDeviceInfo info = (BtDeviceInfo) msg.obj;
if (info.mDevice == null) break;
- AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
"msg: onBluetoothActiveDeviceChange "
+ " state=" + info.mState
// only querying address as this is the only readily available
@@ -1857,7 +1858,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
Log.v(TAG, "onUpdateCommunicationRoute, preferredCommunicationDevice: "
+ preferredCommunicationDevice + " eventSource: " + eventSource);
}
- AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
"onUpdateCommunicationRoute, preferredCommunicationDevice: "
+ preferredCommunicationDevice + " eventSource: " + eventSource)));
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 35da73ef58c0..ce36ff829693 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -43,6 +43,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -308,7 +309,7 @@ public class AudioDeviceInventory {
address = "";
}
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent("BT connected:"
+ AudioService.sDeviceLogger.log(new EventLogger.StringEvent("BT connected:"
+ " addr=" + address
+ " profile=" + btInfo.mProfile
+ " state=" + btInfo.mState
@@ -411,13 +412,13 @@ public class AudioDeviceInventory {
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
address = "";
}
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
"onBluetoothA2dpDeviceConfigChange addr=" + address
+ " event=" + BtHelper.a2dpDeviceEventToString(event)));
synchronized (mDevicesLock) {
if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) {
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
"A2dp config change ignored (scheduled connection change)")
.printLog(TAG));
mmi.set(MediaMetrics.Property.EARLY_RETURN, "A2dp config change ignored")
@@ -459,7 +460,7 @@ public class AudioDeviceInventory {
BtHelper.getName(btDevice), a2dpCodec);
if (res != AudioSystem.AUDIO_STATUS_OK) {
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
"APM handleDeviceConfigChange failed for A2DP device addr=" + address
+ " codec=" + AudioSystem.audioFormatToString(a2dpCodec))
.printLog(TAG));
@@ -471,7 +472,7 @@ public class AudioDeviceInventory {
BluetoothProfile.A2DP, BluetoothProfile.STATE_DISCONNECTED,
musicDevice, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
} else {
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
"APM handleDeviceConfigChange success for A2DP device addr=" + address
+ " codec=" + AudioSystem.audioFormatToString(a2dpCodec))
.printLog(TAG));
@@ -618,7 +619,7 @@ public class AudioDeviceInventory {
@NonNull List<AudioDeviceAttributes> devices) {
final long identity = Binder.clearCallingIdentity();
- AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
"setPreferredDevicesForStrategySync, strategy: " + strategy
+ " devices: " + devices)).printLog(TAG));
final int status = mAudioSystem.setDevicesRoleForStrategy(
@@ -634,7 +635,7 @@ public class AudioDeviceInventory {
/*package*/ int removePreferredDevicesForStrategySync(int strategy) {
final long identity = Binder.clearCallingIdentity();
- AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
"removePreferredDevicesForStrategySync, strategy: "
+ strategy)).printLog(TAG));
@@ -999,13 +1000,13 @@ public class AudioDeviceInventory {
// TODO: log in MediaMetrics once distinction between connection failure and
// double connection is made.
if (res != AudioSystem.AUDIO_STATUS_OK) {
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
"APM failed to make available A2DP device addr=" + address
+ " error=" + res).printLog(TAG));
// TODO: connection failed, stop here
// TODO: return;
} else {
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
"A2DP device addr=" + address + " now available").printLog(TAG));
}
@@ -1046,7 +1047,7 @@ public class AudioDeviceInventory {
if (!deviceToRemoveKey
.equals(mApmConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) {
// removing A2DP device not currently used by AudioPolicy, log but don't act on it
- AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
"A2DP device " + address + " made unavailable, was not used")).printLog(TAG));
mmi.set(MediaMetrics.Property.EARLY_RETURN,
"A2DP device made unavailable, was not used")
@@ -1061,13 +1062,13 @@ public class AudioDeviceInventory {
AudioSystem.DEVICE_STATE_UNAVAILABLE, a2dpCodec);
if (res != AudioSystem.AUDIO_STATUS_OK) {
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
"APM failed to make unavailable A2DP device addr=" + address
+ " error=" + res).printLog(TAG));
// TODO: failed to disconnect, stop here
// TODO: return;
} else {
- AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
"A2DP device addr=" + address + " made unavailable")).printLog(TAG));
}
mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
@@ -1313,7 +1314,7 @@ public class AudioDeviceInventory {
&& !mDeviceBroker.hasAudioFocusUsers()) {
// no media playback, not a "becoming noisy" situation, otherwise it could cause
// the pausing of some apps that are playing remotely
- AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
"dropping ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG));
mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return
return 0;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 82b6fa5f10aa..cbfd17f0a418 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -27,9 +27,9 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
-import static com.android.server.audio.AudioEventLogger.Event.ALOGE;
-import static com.android.server.audio.AudioEventLogger.Event.ALOGI;
-import static com.android.server.audio.AudioEventLogger.Event.ALOGW;
+import static com.android.server.utils.EventLogger.Event.ALOGE;
+import static com.android.server.utils.EventLogger.Event.ALOGI;
+import static com.android.server.utils.EventLogger.Event.ALOGW;
import android.Manifest;
import android.annotation.IntDef;
@@ -185,6 +185,7 @@ import com.android.server.audio.AudioServiceEvents.VolumeEvent;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
import com.android.server.pm.UserManagerService;
+import com.android.server.utils.EventLogger;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
@@ -989,7 +990,7 @@ public class AudioService extends IAudioService.Stub
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings, @Nullable Looper looper,
AppOpsManager appOps) {
- sLifecycleLogger.log(new AudioEventLogger.StringEvent("AudioService()"));
+ sLifecycleLogger.log(new EventLogger.StringEvent("AudioService()"));
mContext = context;
mContentResolver = context.getContentResolver();
mAppOps = appOps;
@@ -1538,14 +1539,14 @@ public class AudioService extends IAudioService.Stub
if (!mSystemReady ||
(AudioSystem.checkAudioFlinger() != AudioSystem.AUDIO_STATUS_OK)) {
Log.e(TAG, "Audioserver died.");
- sLifecycleLogger.log(new AudioEventLogger.StringEvent(
+ sLifecycleLogger.log(new EventLogger.StringEvent(
"onAudioServerDied() audioserver died"));
sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED, SENDMSG_NOOP, 0, 0,
null, 500);
return;
}
Log.i(TAG, "Audioserver started.");
- sLifecycleLogger.log(new AudioEventLogger.StringEvent(
+ sLifecycleLogger.log(new EventLogger.StringEvent(
"onAudioServerDied() audioserver started"));
updateAudioHalPids();
@@ -1775,7 +1776,7 @@ public class AudioService extends IAudioService.Stub
// did it work? check based on status
if (status != AudioSystem.AUDIO_STATUS_OK) {
- sLifecycleLogger.log(new AudioEventLogger.StringEvent(
+ sLifecycleLogger.log(new EventLogger.StringEvent(
caller + ": initStreamVolume failed with " + status + " will retry")
.printLog(ALOGE, TAG));
sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0,
@@ -1789,7 +1790,7 @@ public class AudioService extends IAudioService.Stub
}
// success
- sLifecycleLogger.log(new AudioEventLogger.StringEvent(
+ sLifecycleLogger.log(new EventLogger.StringEvent(
caller + ": initStreamVolume succeeded").printLog(ALOGI, TAG));
}
@@ -1812,7 +1813,7 @@ public class AudioService extends IAudioService.Stub
}
}
if (!success) {
- sLifecycleLogger.log(new AudioEventLogger.StringEvent(
+ sLifecycleLogger.log(new EventLogger.StringEvent(
caller + ": initStreamVolume succeeded but invalid mix/max levels, will retry")
.printLog(ALOGW, TAG));
sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0,
@@ -2763,7 +2764,7 @@ public class AudioService extends IAudioService.Stub
"setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
Binder.getCallingUid(), Binder.getCallingPid(), strategy,
devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
- sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+ sDeviceLogger.log(new EventLogger.StringEvent(logString).printLog(TAG));
if (devices.stream().anyMatch(device ->
device.getRole() == AudioDeviceAttributes.ROLE_INPUT)) {
Log.e(TAG, "Unsupported input routing in " + logString);
@@ -2783,7 +2784,7 @@ public class AudioService extends IAudioService.Stub
public int removePreferredDevicesForStrategy(int strategy) {
final String logString =
String.format("removePreferredDeviceForStrategy strat:%d", strategy);
- sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+ sDeviceLogger.log(new EventLogger.StringEvent(logString).printLog(TAG));
final int status = mDeviceBroker.removePreferredDevicesForStrategySync(strategy);
if (status != AudioSystem.SUCCESS) {
@@ -2849,7 +2850,7 @@ public class AudioService extends IAudioService.Stub
"setPreferredDevicesForCapturePreset u/pid:%d/%d source:%d dev:%s",
Binder.getCallingUid(), Binder.getCallingPid(), capturePreset,
devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
- sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+ sDeviceLogger.log(new EventLogger.StringEvent(logString).printLog(TAG));
if (devices.stream().anyMatch(device ->
device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT)) {
Log.e(TAG, "Unsupported output routing in " + logString);
@@ -2870,7 +2871,7 @@ public class AudioService extends IAudioService.Stub
public int clearPreferredDevicesForCapturePreset(int capturePreset) {
final String logString = String.format(
"removePreferredDeviceForCapturePreset source:%d", capturePreset);
- sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+ sDeviceLogger.log(new EventLogger.StringEvent(logString).printLog(TAG));
final int status = mDeviceBroker.clearPreferredDevicesForCapturePresetSync(capturePreset);
if (status != AudioSystem.SUCCESS) {
@@ -3771,7 +3772,7 @@ public class AudioService extends IAudioService.Stub
return;
}
- final AudioEventLogger.Event event = (device == null)
+ final EventLogger.Event event = (device == null)
? new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
index/*val1*/, flags/*val2*/, callingPackage)
: new DeviceVolumeEvent(streamType, index, device, callingPackage);
@@ -6887,7 +6888,7 @@ public class AudioService extends IAudioService.Stub
// verify arguments
Objects.requireNonNull(device);
AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
- sVolumeLogger.log(new AudioEventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
+ sVolumeLogger.log(new EventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
+ AudioSystem.getOutputDeviceName(device.getInternalType()) + " addr:"
+ device.getAddress() + " behavior:"
+ AudioDeviceVolumeManager.volumeBehaviorName(deviceVolumeBehavior)
@@ -6943,7 +6944,7 @@ public class AudioService extends IAudioService.Stub
}
// log event and caller
- sDeviceLogger.log(new AudioEventLogger.StringEvent(
+ sDeviceLogger.log(new EventLogger.StringEvent(
"Volume behavior " + deviceVolumeBehavior + " for dev=0x"
+ Integer.toHexString(audioSystemDeviceOut) + " from:" + caller));
// make sure we have a volume entry for this device, and that volume is updated according
@@ -7589,7 +7590,7 @@ public class AudioService extends IAudioService.Stub
final int status = AudioSystem.initStreamVolume(
streamType, mIndexMin / 10, mIndexMax / 10);
if (status != AudioSystem.AUDIO_STATUS_OK) {
- sLifecycleLogger.log(new AudioEventLogger.StringEvent(
+ sLifecycleLogger.log(new EventLogger.StringEvent(
"VSS() stream:" + streamType + " initStreamVolume=" + status)
.printLog(ALOGE, TAG));
sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0,
@@ -8178,10 +8179,10 @@ public class AudioService extends IAudioService.Stub
streamState.setIndex(index, update.mDevice, update.mCaller,
// trusted as index is always validated before message is posted
true /*hasModifyAudioSettings*/);
- sVolumeLogger.log(new AudioEventLogger.StringEvent(update.mCaller + " dev:0x"
+ sVolumeLogger.log(new EventLogger.StringEvent(update.mCaller + " dev:0x"
+ Integer.toHexString(update.mDevice) + " volIdx:" + index));
} else {
- sVolumeLogger.log(new AudioEventLogger.StringEvent(update.mCaller
+ sVolumeLogger.log(new EventLogger.StringEvent(update.mCaller
+ " update vol on dev:0x" + Integer.toHexString(update.mDevice)));
}
setDeviceVolume(streamState, update.mDevice);
@@ -8628,7 +8629,7 @@ public class AudioService extends IAudioService.Stub
private void avrcpSupportsAbsoluteVolume(String address, boolean support) {
// address is not used for now, but may be used when multiple a2dp devices are supported
- sVolumeLogger.log(new AudioEventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
+ sVolumeLogger.log(new EventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
+ address + " support=" + support).printLog(TAG));
mDeviceBroker.setAvrcpAbsoluteVolumeSupported(support);
setAvrcpAbsoluteVolumeSupported(support);
@@ -10049,29 +10050,36 @@ public class AudioService extends IAudioService.Stub
static final int LOG_NB_EVENTS_DYN_POLICY = 10;
static final int LOG_NB_EVENTS_SPATIAL = 30;
- static final AudioEventLogger sLifecycleLogger = new AudioEventLogger(LOG_NB_EVENTS_LIFECYCLE,
+ static final EventLogger
+ sLifecycleLogger = new EventLogger(LOG_NB_EVENTS_LIFECYCLE,
"audio services lifecycle");
- final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE,
+ final private EventLogger
+ mModeLogger = new EventLogger(LOG_NB_EVENTS_PHONE_STATE,
"phone state (logged after successful call to AudioSystem.setPhoneState(int, int))");
// logs for wired + A2DP device connections:
// - wired: logged before onSetWiredDeviceConnectionState() is executed
// - A2DP: logged at reception of method call
- /*package*/ static final AudioEventLogger sDeviceLogger = new AudioEventLogger(
+ /*package*/ static final EventLogger
+ sDeviceLogger = new EventLogger(
LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP/hearing aid device connection");
- static final AudioEventLogger sForceUseLogger = new AudioEventLogger(
+ static final EventLogger
+ sForceUseLogger = new EventLogger(
LOG_NB_EVENTS_FORCE_USE,
"force use (logged before setForceUse() is executed)");
- static final AudioEventLogger sVolumeLogger = new AudioEventLogger(LOG_NB_EVENTS_VOLUME,
+ static final EventLogger
+ sVolumeLogger = new EventLogger(LOG_NB_EVENTS_VOLUME,
"volume changes (logged when command received by AudioService)");
- static final AudioEventLogger sSpatialLogger = new AudioEventLogger(LOG_NB_EVENTS_SPATIAL,
+ static final EventLogger
+ sSpatialLogger = new EventLogger(LOG_NB_EVENTS_SPATIAL,
"spatial audio");
- final private AudioEventLogger mDynPolicyLogger = new AudioEventLogger(LOG_NB_EVENTS_DYN_POLICY,
+ final private EventLogger
+ mDynPolicyLogger = new EventLogger(LOG_NB_EVENTS_DYN_POLICY,
"dynamic policy events (logged when command received by AudioService)");
private static final String[] RINGER_MODE_NAMES = new String[] {
@@ -10656,7 +10664,7 @@ public class AudioService extends IAudioService.Stub
pcb.asBinder().linkToDeath(app, 0/*flags*/);
// logging after registration so we have the registration id
- mDynPolicyLogger.log((new AudioEventLogger.StringEvent("registerAudioPolicy for "
+ mDynPolicyLogger.log((new EventLogger.StringEvent("registerAudioPolicy for "
+ pcb.asBinder() + " u/pid:" + Binder.getCallingUid() + "/"
+ Binder.getCallingPid() + " with config:" + app.toCompactLogString()))
.printLog(TAG));
@@ -10854,7 +10862,7 @@ public class AudioService extends IAudioService.Stub
private void unregisterAudioPolicyInt(@NonNull IAudioPolicyCallback pcb, String operationName) {
- mDynPolicyLogger.log((new AudioEventLogger.StringEvent(operationName + " for "
+ mDynPolicyLogger.log((new EventLogger.StringEvent(operationName + " for "
+ pcb.asBinder()).printLog(TAG)));
synchronized (mAudioPolicies) {
AudioPolicyProxy app = mAudioPolicies.remove(pcb.asBinder());
@@ -11382,7 +11390,7 @@ public class AudioService extends IAudioService.Stub
}
public void binderDied() {
- mDynPolicyLogger.log((new AudioEventLogger.StringEvent("AudioPolicy "
+ mDynPolicyLogger.log((new EventLogger.StringEvent("AudioPolicy "
+ mPolicyCallback.asBinder() + " died").printLog(TAG)));
release();
}
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 30a9e0a70e96..b920517166ec 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -24,11 +24,12 @@ import android.media.AudioSystem;
import android.media.MediaMetrics;
import com.android.server.audio.AudioDeviceInventory.WiredDeviceConnectionState;
+import com.android.server.utils.EventLogger;
public class AudioServiceEvents {
- final static class PhoneStateEvent extends AudioEventLogger.Event {
+ final static class PhoneStateEvent extends EventLogger.Event {
static final int MODE_SET = 0;
static final int MODE_IN_COMMUNICATION_TIMEOUT = 1;
@@ -108,7 +109,7 @@ public class AudioServiceEvents {
}
}
- final static class WiredDevConnectEvent extends AudioEventLogger.Event {
+ final static class WiredDevConnectEvent extends EventLogger.Event {
final WiredDeviceConnectionState mState;
WiredDevConnectEvent(WiredDeviceConnectionState state) {
@@ -127,7 +128,7 @@ public class AudioServiceEvents {
}
}
- final static class ForceUseEvent extends AudioEventLogger.Event {
+ final static class ForceUseEvent extends EventLogger.Event {
final int mUsage;
final int mConfig;
final String mReason;
@@ -147,7 +148,7 @@ public class AudioServiceEvents {
}
}
- static final class DeviceVolumeEvent extends AudioEventLogger.Event {
+ static final class DeviceVolumeEvent extends EventLogger.Event {
final int mStream;
final int mVolIndex;
final String mDeviceNativeType;
@@ -184,7 +185,7 @@ public class AudioServiceEvents {
}
}
- final static class VolumeEvent extends AudioEventLogger.Event {
+ final static class VolumeEvent extends EventLogger.Event {
static final int VOL_ADJUST_SUGG_VOL = 0;
static final int VOL_ADJUST_STREAM_VOL = 1;
static final int VOL_SET_STREAM_VOL = 2;
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 6e85361e9e91..399829e32588 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -39,6 +39,7 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
import java.util.Collections;
@@ -262,13 +263,13 @@ public class BtHelper {
/*package*/ synchronized void setAvrcpAbsoluteVolumeIndex(int index) {
if (mA2dp == null) {
if (AudioService.DEBUG_VOL) {
- AudioService.sVolumeLogger.log(new AudioEventLogger.StringEvent(
+ AudioService.sVolumeLogger.log(new EventLogger.StringEvent(
"setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp").printLog(TAG));
return;
}
}
if (!mAvrcpAbsVolSupported) {
- AudioService.sVolumeLogger.log(new AudioEventLogger.StringEvent(
+ AudioService.sVolumeLogger.log(new EventLogger.StringEvent(
"setAvrcpAbsoluteVolumeIndex: abs vol not supported ").printLog(TAG));
return;
}
@@ -392,14 +393,14 @@ public class BtHelper {
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
@NonNull String eventSource) {
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
+ AudioService.sDeviceLogger.log(new EventLogger.StringEvent(eventSource));
return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
}
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
+ AudioService.sDeviceLogger.log(new EventLogger.StringEvent(eventSource));
return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
}
@@ -674,7 +675,7 @@ public class BtHelper {
case BluetoothProfile.HEADSET:
case BluetoothProfile.HEARING_AID:
case BluetoothProfile.LE_AUDIO:
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+ AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
"BT profile service: connecting "
+ BluetoothProfile.getProfileName(profile) + " profile"));
mDeviceBroker.postBtProfileConnected(profile, proxy);
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
index 00cb280236d7..e54ee869fef2 100644
--- a/services/core/java/com/android/server/audio/FadeOutManager.java
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -24,6 +24,7 @@ import android.media.VolumeShaper;
import android.util.Log;
import com.android.internal.util.ArrayUtils;
+import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -262,7 +263,7 @@ public final class FadeOutManager {
if (apc != null) {
try {
PlaybackActivityMonitor.sEventLogger.log(
- (new AudioEventLogger.StringEvent("unfading out piid:"
+ (new EventLogger.StringEvent("unfading out piid:"
+ piid)).printLog(TAG));
apc.getPlayerProxy().applyVolumeShaper(
FADEOUT_VSHAPE,
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 69a4c23cf867..1ca27dd7112c 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -40,6 +40,7 @@ import android.provider.Settings;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
import java.text.DateFormat;
@@ -170,7 +171,8 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
*/
private static final int MAX_STACK_SIZE = 100;
- private static final AudioEventLogger mEventLogger = new AudioEventLogger(50,
+ private static final EventLogger
+ mEventLogger = new EventLogger(50,
"focus commands as seen by MediaFocusControl");
private static final String mMetricsId = MediaMetrics.Name.AUDIO_FOCUS;
@@ -183,7 +185,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
final FocusRequester focusOwner = stackIterator.next();
if (focusOwner.hasSameUid(uid) && focusOwner.hasSamePackage(packageName)) {
clientsToRemove.add(focusOwner.getClientId());
- mEventLogger.log((new AudioEventLogger.StringEvent(
+ mEventLogger.log((new EventLogger.StringEvent(
"focus owner:" + focusOwner.getClientId()
+ " in uid:" + uid + " pack: " + packageName
+ " getting AUDIOFOCUS_LOSS due to app suspension"))
@@ -431,7 +433,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
FocusRequester fr = stackIterator.next();
if(fr.hasSameBinder(cb)) {
Log.i(TAG, "AudioFocus removeFocusStackEntryOnDeath(): removing entry for " + cb);
- mEventLogger.log(new AudioEventLogger.StringEvent(
+ mEventLogger.log(new EventLogger.StringEvent(
"focus requester:" + fr.getClientId()
+ " in uid:" + fr.getClientUid()
+ " pack:" + fr.getPackageName()
@@ -468,7 +470,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
final FocusRequester fr = owner.getValue();
if (fr.hasSameBinder(cb)) {
ownerIterator.remove();
- mEventLogger.log(new AudioEventLogger.StringEvent(
+ mEventLogger.log(new EventLogger.StringEvent(
"focus requester:" + fr.getClientId()
+ " in uid:" + fr.getClientUid()
+ " pack:" + fr.getPackageName()
@@ -966,7 +968,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
// supposed to be alone in bitfield
final int uid = (flags == AudioManager.AUDIOFOCUS_FLAG_TEST)
? testUid : Binder.getCallingUid();
- mEventLogger.log((new AudioEventLogger.StringEvent(
+ mEventLogger.log((new EventLogger.StringEvent(
"requestAudioFocus() from uid/pid " + uid
+ "/" + Binder.getCallingPid()
+ " AA=" + aa.usageToString() + "/" + aa.contentTypeToString()
@@ -1141,7 +1143,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
.record();
// AudioAttributes are currently ignored, to be used for zones / a11y
- mEventLogger.log((new AudioEventLogger.StringEvent(
+ mEventLogger.log((new EventLogger.StringEvent(
"abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
+ "/" + Binder.getCallingPid()
+ " clientId=" + clientId))
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index e5d40d93909f..1af8c593f96b 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -53,6 +53,7 @@ import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
+import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
import java.text.DateFormat;
@@ -156,7 +157,7 @@ public final class PlaybackActivityMonitor
if (index >= 0) {
if (!disable) {
if (DEBUG) { // hidden behind DEBUG, too noisy otherwise
- sEventLogger.log(new AudioEventLogger.StringEvent("unbanning uid:" + uid));
+ sEventLogger.log(new EventLogger.StringEvent("unbanning uid:" + uid));
}
mBannedUids.remove(index);
// nothing else to do, future playback requests from this uid are ok
@@ -167,7 +168,7 @@ public final class PlaybackActivityMonitor
checkBanPlayer(apc, uid);
}
if (DEBUG) { // hidden behind DEBUG, too noisy otherwise
- sEventLogger.log(new AudioEventLogger.StringEvent("banning uid:" + uid));
+ sEventLogger.log(new EventLogger.StringEvent("banning uid:" + uid));
}
mBannedUids.add(new Integer(uid));
} // no else to handle, uid already not in list, so enabling again is no-op
@@ -331,7 +332,7 @@ public final class PlaybackActivityMonitor
for (Integer uidInteger: mBannedUids) {
if (checkBanPlayer(apc, uidInteger.intValue())) {
// player was banned, do not update its state
- sEventLogger.log(new AudioEventLogger.StringEvent(
+ sEventLogger.log(new EventLogger.StringEvent(
"not starting piid:" + piid + " ,is banned"));
return;
}
@@ -420,7 +421,7 @@ public final class PlaybackActivityMonitor
synchronized(mPlayerLock) {
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
if (checkConfigurationCaller(piid, apc, binderUid)) {
- sEventLogger.log(new AudioEventLogger.StringEvent(
+ sEventLogger.log(new EventLogger.StringEvent(
"releasing player piid:" + piid));
mPlayers.remove(new Integer(piid));
mDuckingManager.removeReleased(apc);
@@ -443,7 +444,7 @@ public final class PlaybackActivityMonitor
/*package*/ void onAudioServerDied() {
sEventLogger.log(
- new AudioEventLogger.StringEvent(
+ new EventLogger.StringEvent(
"clear port id to piid map"));
synchronized (mPlayerLock) {
if (DEBUG) {
@@ -767,7 +768,7 @@ public final class PlaybackActivityMonitor
}
if (mute) {
try {
- sEventLogger.log((new AudioEventLogger.StringEvent("call: muting piid:"
+ sEventLogger.log((new EventLogger.StringEvent("call: muting piid:"
+ piid + " uid:" + apc.getClientUid())).printLog(TAG));
apc.getPlayerProxy().setVolume(0.0f);
mMutedPlayers.add(new Integer(piid));
@@ -792,7 +793,7 @@ public final class PlaybackActivityMonitor
final AudioPlaybackConfiguration apc = mPlayers.get(piid);
if (apc != null) {
try {
- sEventLogger.log(new AudioEventLogger.StringEvent("call: unmuting piid:"
+ sEventLogger.log(new EventLogger.StringEvent("call: unmuting piid:"
+ piid).printLog(TAG));
apc.getPlayerProxy().setVolume(1.0f);
} catch (Exception e) {
@@ -1095,7 +1096,7 @@ public final class PlaybackActivityMonitor
final AudioPlaybackConfiguration apc = players.get(piid);
if (apc != null) {
try {
- sEventLogger.log((new AudioEventLogger.StringEvent("unducking piid:"
+ sEventLogger.log((new EventLogger.StringEvent("unducking piid:"
+ piid)).printLog(TAG));
apc.getPlayerProxy().applyVolumeShaper(
DUCK_ID,
@@ -1122,7 +1123,7 @@ public final class PlaybackActivityMonitor
//=================================================================
// For logging
- private static final class PlayerEvent extends AudioEventLogger.Event {
+ private static final class PlayerEvent extends EventLogger.Event {
// only keeping the player interface ID as it uniquely identifies the player in the event
final int mPlayerIId;
final int mEvent;
@@ -1180,7 +1181,7 @@ public final class PlaybackActivityMonitor
}
}
- private static final class PlayerOpPlayAudioEvent extends AudioEventLogger.Event {
+ private static final class PlayerOpPlayAudioEvent extends EventLogger.Event {
// only keeping the player interface ID as it uniquely identifies the player in the event
final int mPlayerIId;
final boolean mHasOp;
@@ -1200,7 +1201,7 @@ public final class PlaybackActivityMonitor
}
}
- private static final class NewPlayerEvent extends AudioEventLogger.Event {
+ private static final class NewPlayerEvent extends EventLogger.Event {
private final int mPlayerIId;
private final int mPlayerType;
private final int mClientUid;
@@ -1227,7 +1228,7 @@ public final class PlaybackActivityMonitor
}
}
- private abstract static class VolumeShaperEvent extends AudioEventLogger.Event {
+ private abstract static class VolumeShaperEvent extends EventLogger.Event {
private final int mPlayerIId;
private final boolean mSkipRamp;
private final int mClientUid;
@@ -1272,7 +1273,7 @@ public final class PlaybackActivityMonitor
}
}
- private static final class AudioAttrEvent extends AudioEventLogger.Event {
+ private static final class AudioAttrEvent extends EventLogger.Event {
private final int mPlayerIId;
private final AudioAttributes mPlayerAttr;
@@ -1287,7 +1288,7 @@ public final class PlaybackActivityMonitor
}
}
- private static final class MuteAwaitConnectionEvent extends AudioEventLogger.Event {
+ private static final class MuteAwaitConnectionEvent extends EventLogger.Event {
private final @NonNull int[] mUsagesToMute;
MuteAwaitConnectionEvent(@NonNull int[] usagesToMute) {
@@ -1300,7 +1301,8 @@ public final class PlaybackActivityMonitor
}
}
- static final AudioEventLogger sEventLogger = new AudioEventLogger(100,
+ static final EventLogger
+ sEventLogger = new EventLogger(100,
"playback activity as reported through PlayerBase");
//==========================================================================================
@@ -1367,7 +1369,7 @@ public final class PlaybackActivityMonitor
for (int usage : mMutedUsagesAwaitingConnection) {
if (usage == apc.getAudioAttributes().getUsage()) {
try {
- sEventLogger.log((new AudioEventLogger.StringEvent(
+ sEventLogger.log((new EventLogger.StringEvent(
"awaiting connection: muting piid:"
+ apc.getPlayerInterfaceId()
+ " uid:" + apc.getClientUid())).printLog(TAG));
@@ -1392,7 +1394,7 @@ public final class PlaybackActivityMonitor
continue;
}
try {
- sEventLogger.log(new AudioEventLogger.StringEvent(
+ sEventLogger.log(new EventLogger.StringEvent(
"unmuting piid:" + piid).printLog(TAG));
apc.getPlayerProxy().applyVolumeShaper(MUTE_AWAIT_CONNECTION_VSHAPE,
VolumeShaper.Operation.REVERSE);
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 5620dc36c971..2ba8882ae14f 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -30,6 +30,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.server.utils.EventLogger;
+
import java.io.PrintWriter;
import java.text.DateFormat;
import java.util.ArrayList;
@@ -587,7 +589,7 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin
/**
* Inner class for recording event logging
*/
- private static final class RecordingEvent extends AudioEventLogger.Event {
+ private static final class RecordingEvent extends EventLogger.Event {
private final int mRecEvent;
private final int mRIId;
private final int mClientUid;
@@ -641,6 +643,7 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin
}
}
- private static final AudioEventLogger sEventLogger = new AudioEventLogger(50,
+ private static final EventLogger
+ sEventLogger = new EventLogger(50,
"recording activity received by AudioService");
}
diff --git a/services/core/java/com/android/server/audio/SoundEffectsHelper.java b/services/core/java/com/android/server/audio/SoundEffectsHelper.java
index 7031e02af05a..93eba50ac6dd 100644
--- a/services/core/java/com/android/server/audio/SoundEffectsHelper.java
+++ b/services/core/java/com/android/server/audio/SoundEffectsHelper.java
@@ -34,6 +34,7 @@ import android.util.Log;
import android.util.PrintWriterPrinter;
import com.android.internal.util.XmlUtils;
+import com.android.server.utils.EventLogger;
import org.xmlpull.v1.XmlPullParserException;
@@ -74,7 +75,8 @@ class SoundEffectsHelper {
void run(boolean success);
}
- private final AudioEventLogger mSfxLogger = new AudioEventLogger(
+ private final EventLogger
+ mSfxLogger = new EventLogger(
AudioManager.NUM_SOUND_EFFECTS + 10, "Sound Effects Loading");
private final Context mContext;
@@ -162,7 +164,7 @@ class SoundEffectsHelper {
}
private void logEvent(String msg) {
- mSfxLogger.log(new AudioEventLogger.StringEvent(msg));
+ mSfxLogger.log(new EventLogger.StringEvent(msg));
}
// All the methods below run on the worker thread
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 8e8fd05bf72e..1563d33d93f0 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -47,6 +47,7 @@ import android.util.Pair;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -1711,7 +1712,7 @@ public class SpatializerHelper {
}
private static String logloge(String msg) {
- AudioService.sSpatialLogger.loglog(msg, AudioEventLogger.Event.ALOGE, TAG);
+ AudioService.sSpatialLogger.loglog(msg, EventLogger.Event.ALOGE, TAG);
return msg;
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6a53978175af..6795b6b4158d 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -21,6 +21,7 @@ import static android.Manifest.permission.CONTROL_VPN;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
@@ -51,6 +52,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
+import android.net.ConnectivityDiagnosticsManager;
import android.net.ConnectivityManager;
import android.net.DnsResolver;
import android.net.INetd;
@@ -234,6 +236,7 @@ public class Vpn {
private final Context mContext;
private final ConnectivityManager mConnectivityManager;
private final AppOpsManager mAppOpsManager;
+ private final ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager;
// The context is for specific user which is created from mUserId
private final Context mUserIdContext;
@VisibleForTesting final Dependencies mDeps;
@@ -549,6 +552,8 @@ public class Vpn {
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
+ mConnectivityDiagnosticsManager =
+ mContext.getSystemService(ConnectivityDiagnosticsManager.class);
mDeps = deps;
mNms = netService;
mNetd = netd;
@@ -2739,6 +2744,7 @@ public class Vpn {
@Nullable private IkeSessionWrapper mSession;
@Nullable private IkeSessionConnectionInfo mIkeConnectionInfo;
+ @Nullable private VpnConnectivityDiagnosticsCallback mDiagnosticsCallback;
// mMobikeEnabled can only be updated after IKE AUTH is finished.
private boolean mMobikeEnabled = false;
@@ -2797,6 +2803,15 @@ public class Vpn {
mConnectivityManager.registerSystemDefaultNetworkCallback(mNetworkCallback,
new Handler(mLooper));
}
+
+ // DiagnosticsCallback may return more than one alive VPNs, but VPN will filter based on
+ // Network object.
+ final NetworkRequest diagRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_VPN)
+ .removeCapability(NET_CAPABILITY_NOT_VPN).build();
+ mDiagnosticsCallback = new VpnConnectivityDiagnosticsCallback();
+ mConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback(
+ diagRequest, mExecutor, mDiagnosticsCallback);
}
private boolean isActiveNetwork(@Nullable Network network) {
@@ -3066,22 +3081,28 @@ public class Vpn {
return;
}
- try {
- if (mSession != null && mMobikeEnabled) {
- // IKE session can schedule a migration event only when IKE AUTH is finished
- // and mMobikeEnabled is true.
- Log.d(
- TAG,
- "Migrate IKE Session with token "
- + mCurrentToken
- + " to network "
- + underlyingNetwork);
- mSession.setNetwork(underlyingNetwork);
- return;
- }
+ if (maybeMigrateIkeSession(underlyingNetwork)) return;
+
+ startIkeSession(underlyingNetwork);
+ }
- Log.d(TAG, "Start new IKE session on network " + underlyingNetwork);
+ boolean maybeMigrateIkeSession(@NonNull Network underlyingNetwork) {
+ if (mSession == null || !mMobikeEnabled) return false;
+ // IKE session can schedule a migration event only when IKE AUTH is finished
+ // and mMobikeEnabled is true.
+ Log.d(TAG, "Migrate IKE Session with token "
+ + mCurrentToken
+ + " to network "
+ + underlyingNetwork);
+ mSession.setNetwork(underlyingNetwork);
+ return true;
+ }
+
+ private void startIkeSession(@NonNull Network underlyingNetwork) {
+ Log.d(TAG, "Start new IKE session on network " + underlyingNetwork);
+
+ try {
// Clear mInterface to prevent Ikev2VpnRunner being cleared when
// interfaceRemoved() is called.
synchronized (Vpn.this) {
@@ -3169,6 +3190,28 @@ public class Vpn {
mUnderlyingLinkProperties = lp;
}
+ class VpnConnectivityDiagnosticsCallback
+ extends ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
+ // The callback runs in the executor thread.
+ @Override
+ public void onDataStallSuspected(
+ ConnectivityDiagnosticsManager.DataStallReport report) {
+ synchronized (Vpn.this) {
+ // Ignore stale runner.
+ if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
+
+ // Handle the report only for current VPN network.
+ if (mNetworkAgent != null
+ && mNetworkAgent.getNetwork().equals(report.getNetwork())) {
+ Log.d(TAG, "Data stall suspected");
+
+ // Trigger MOBIKE.
+ maybeMigrateIkeSession(mActiveNetwork);
+ }
+ }
+ }
+ }
+
/**
* Handles loss of the default underlying network
*
@@ -3463,6 +3506,8 @@ public class Vpn {
resetIkeState();
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
+ mDiagnosticsCallback);
mExecutor.shutdown();
}
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
new file mode 100644
index 000000000000..87cbbfe1c62a
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import com.android.server.display.brightness.BrightnessReason;
+
+import java.util.Objects;
+
+/**
+ * A state class representing a set of brightness related entities that are decided at runtime by
+ * the DisplayBrightnessModeStrategies when updating the brightness.
+ */
+public final class DisplayBrightnessState {
+ private final float mBrightness;
+ private final float mSdrBrightness;
+ private final BrightnessReason mBrightnessReason;
+
+ private DisplayBrightnessState(Builder builder) {
+ this.mBrightness = builder.getBrightness();
+ this.mSdrBrightness = builder.getSdrBrightness();
+ this.mBrightnessReason = builder.getBrightnessReason();
+ }
+
+ /**
+ * Gets the brightness
+ */
+ public float getBrightness() {
+ return mBrightness;
+ }
+
+ /**
+ * Gets the sdr brightness
+ */
+ public float getSdrBrightness() {
+ return mSdrBrightness;
+ }
+
+ /**
+ * Gets the {@link BrightnessReason}
+ */
+ public BrightnessReason getBrightnessReason() {
+ return mBrightnessReason;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:");
+ stringBuilder.append("\n brightness:");
+ stringBuilder.append(getBrightness());
+ stringBuilder.append("\n sdrBrightness:");
+ stringBuilder.append(getSdrBrightness());
+ stringBuilder.append("\n brightnessReason:");
+ stringBuilder.append(getBrightnessReason());
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Checks whether the two objects have the same values.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof DisplayBrightnessState)) {
+ return false;
+ }
+
+ DisplayBrightnessState
+ displayBrightnessState = (DisplayBrightnessState) other;
+
+ if (mBrightness != displayBrightnessState.getBrightness()) {
+ return false;
+ }
+ if (mSdrBrightness != displayBrightnessState.getSdrBrightness()) {
+ return false;
+ }
+ if (!mBrightnessReason.equals(displayBrightnessState.getBrightnessReason())) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mBrightness, mSdrBrightness, mBrightnessReason);
+ }
+
+ /**
+ * A DisplayBrightnessState's builder class.
+ */
+ public static class Builder {
+ private float mBrightness;
+ private float mSdrBrightness;
+ private BrightnessReason mBrightnessReason = new BrightnessReason();
+
+ /**
+ * Gets the brightness
+ */
+ public float getBrightness() {
+ return mBrightness;
+ }
+
+ /**
+ * Sets the brightness
+ *
+ * @param brightness The brightness to be associated with DisplayBrightnessState's
+ * builder
+ */
+ public Builder setBrightness(float brightness) {
+ this.mBrightness = brightness;
+ return this;
+ }
+
+ /**
+ * Gets the sdr brightness
+ */
+ public float getSdrBrightness() {
+ return mSdrBrightness;
+ }
+
+ /**
+ * Sets the sdr brightness
+ *
+ * @param sdrBrightness The sdr brightness to be associated with DisplayBrightnessState's
+ * builder
+ */
+ public Builder setSdrBrightness(float sdrBrightness) {
+ this.mSdrBrightness = sdrBrightness;
+ return this;
+ }
+
+ /**
+ * Gets the {@link BrightnessReason}
+ */
+ public BrightnessReason getBrightnessReason() {
+ return mBrightnessReason;
+ }
+
+ /**
+ * Sets the {@link BrightnessReason}
+ *
+ * @param brightnessReason The brightness reason {@link BrightnessReason} to be
+ * associated with the builder
+ */
+ public Builder setBrightnessReason(BrightnessReason brightnessReason) {
+ this.mBrightnessReason = brightnessReason;
+ return this;
+ }
+
+ /**
+ * This is used to construct an immutable DisplayBrightnessState object from its builder
+ */
+ public DisplayBrightnessState build() {
+ return new DisplayBrightnessState(this);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayControl.java b/services/core/java/com/android/server/display/DisplayControl.java
index a060f076d4fb..864510ea96b9 100644
--- a/services/core/java/com/android/server/display/DisplayControl.java
+++ b/services/core/java/com/android/server/display/DisplayControl.java
@@ -30,6 +30,8 @@ public class DisplayControl {
private static native IBinder nativeCreateDisplay(String name, boolean secure);
private static native void nativeDestroyDisplay(IBinder displayToken);
private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
+ private static native long[] nativeGetPhysicalDisplayIds();
+ private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
/**
* Create a display in SurfaceFlinger.
@@ -63,4 +65,18 @@ public class DisplayControl {
public static void overrideHdrTypes(@NonNull IBinder displayToken, @NonNull int[] modes) {
nativeOverrideHdrTypes(displayToken, modes);
}
+
+ /**
+ * Gets all the physical display ids.
+ */
+ public static long[] getPhysicalDisplayIds() {
+ return nativeGetPhysicalDisplayIds();
+ }
+
+ /**
+ * Gets the display's token from the physical display id
+ */
+ public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
+ return nativeGetPhysicalDisplayToken(physicalDisplayId);
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 81219ba687f6..bf981be54be3 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -150,7 +150,7 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <quirk>canSetBrightnessViaHwc</quirk>
* </quirks>
*
- * <autoBrightness>
+ * <autoBrightness enable="true">
* <brighteningLightDebounceMillis>
* 2000
* </brighteningLightDebounceMillis>
@@ -507,6 +507,11 @@ public class DisplayDeviceConfig {
private long mAutoBrightnessDarkeningLightDebounce =
INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE;
+ // This setting allows non-default displays to have autobrightness enabled.
+ private boolean mAutoBrightnessAvailable = false;
+ // This stores the raw value loaded from the config file - true if not written.
+ private boolean mDdcAutoBrightnessAvailable = true;
+
// Brightness Throttling data may be updated via the DeviceConfig. Here we store the original
// data, which comes from the ddc, and the current one, which may be the DeviceConfig
// overwritten value.
@@ -1119,6 +1124,10 @@ public class DisplayDeviceConfig {
return mProximitySensor;
}
+ boolean isAutoBrightnessAvailable() {
+ return mAutoBrightnessAvailable;
+ }
+
/**
* @param quirkValue The quirk to test.
* @return {@code true} if the specified quirk is present in this configuration, {@code false}
@@ -1271,6 +1280,8 @@ public class DisplayDeviceConfig {
+ mAutoBrightnessDarkeningLightDebounce
+ ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux)
+ ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
+ + ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable
+ + ", mAutoBrightnessAvailable= " + mAutoBrightnessAvailable
+ "}";
}
@@ -1349,6 +1360,7 @@ public class DisplayDeviceConfig {
loadBrightnessChangeThresholdsFromXml();
setProxSensorUnspecified();
loadAutoBrightnessConfigsFromConfigXml();
+ loadAutoBrightnessAvailableFromConfigXml();
mLoadedFrom = "<config.xml>";
}
@@ -1367,6 +1379,7 @@ public class DisplayDeviceConfig {
setSimpleMappingStrategyValues();
loadAmbientLightSensorFromConfigXml();
setProxSensorUnspecified();
+ loadAutoBrightnessAvailableFromConfigXml();
}
private void copyUninitializedValuesFromSecondaryConfig(DisplayConfiguration defaultConfig) {
@@ -1559,9 +1572,11 @@ public class DisplayDeviceConfig {
}
private void loadAutoBrightnessConfigValues(DisplayConfiguration config) {
- loadAutoBrightnessBrighteningLightDebounce(config.getAutoBrightness());
- loadAutoBrightnessDarkeningLightDebounce(config.getAutoBrightness());
- loadAutoBrightnessDisplayBrightnessMapping(config.getAutoBrightness());
+ final AutoBrightness autoBrightness = config.getAutoBrightness();
+ loadAutoBrightnessBrighteningLightDebounce(autoBrightness);
+ loadAutoBrightnessDarkeningLightDebounce(autoBrightness);
+ loadAutoBrightnessDisplayBrightnessMapping(autoBrightness);
+ loadEnableAutoBrightness(autoBrightness);
}
/**
@@ -1623,6 +1638,11 @@ public class DisplayDeviceConfig {
}
}
+ private void loadAutoBrightnessAvailableFromConfigXml() {
+ mAutoBrightnessAvailable = mContext.getResources().getBoolean(
+ R.bool.config_automatic_brightness_available);
+ }
+
private void loadBrightnessMapFromConfigXml() {
// Use the config.xml mapping
final Resources res = mContext.getResources();
@@ -2262,6 +2282,20 @@ public class DisplayDeviceConfig {
return levels;
}
+ private void loadEnableAutoBrightness(AutoBrightness autobrightness) {
+ // mDdcAutoBrightnessAvailable is initialised to true, so that we fallback to using the
+ // config.xml values if the autobrightness tag is not defined in the ddc file.
+ // Autobrightness can still be turned off globally via config_automatic_brightness_available
+ mDdcAutoBrightnessAvailable = true;
+ if (autobrightness != null) {
+ mDdcAutoBrightnessAvailable = autobrightness.getEnabled();
+ }
+
+ mAutoBrightnessAvailable = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_automatic_brightness_available)
+ && mDdcAutoBrightnessAvailable;
+ }
+
static class SensorData {
public String type;
public String name;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 84891c7c74a3..9a2de10983e9 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1461,8 +1461,8 @@ public final class DisplayManagerService extends SystemService {
// Register default display adapters.
synchronized (mSyncRoot) {
// main display adapter
- registerDisplayAdapterLocked(new LocalDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayDeviceRepo));
+ registerDisplayAdapterLocked(mInjector.getLocalDisplayAdapter(mSyncRoot, mContext,
+ mHandler, mDisplayDeviceRepo));
// Standalone VR devices rely on a virtual display as their primary display for
// 2D UI. We register virtual display adapter along side the main display adapter
@@ -2520,6 +2520,11 @@ public final class DisplayManagerService extends SystemService {
return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
}
+ LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
+ Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
+ }
+
long getDefaultDisplayDelayTimeout() {
return WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT;
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 4752044003a3..422e98ff97ba 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -566,13 +566,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mScreenBrightnessForVrRangeMinimum = clampAbsoluteBrightness(
pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR));
- // Check the setting, but also verify that it is the default display. Only the default
- // display has an automatic brightness controller running.
- // TODO: b/179021925 - Fix to work with multiple displays
- mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
- com.android.internal.R.bool.config_automatic_brightness_available)
- && mDisplayId == Display.DEFAULT_DISPLAY;
-
mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);
@@ -952,6 +945,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
private void setUpAutoBrightness(Resources resources, Handler handler) {
+ mUseSoftwareAutoBrightnessConfig = mDisplayDeviceConfig.isAutoBrightnessAvailable();
+
if (!mUseSoftwareAutoBrightnessConfig) {
return;
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 172b4be99c3b..23c020eb8692 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -542,13 +542,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
mScreenBrightnessForVrRangeMinimum = clampAbsoluteBrightness(
pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR));
- // Check the setting, but also verify that it is the default display. Only the default
- // display has an automatic brightness controller running.
- // TODO: b/179021925 - Fix to work with multiple displays
- mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
- R.bool.config_automatic_brightness_available)
- && mDisplayId == Display.DEFAULT_DISPLAY;
-
mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
R.bool.config_allowAutoBrightnessWhileDozing);
@@ -928,6 +921,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
}
private void setUpAutoBrightness(Resources resources, Handler handler) {
+ mUseSoftwareAutoBrightnessConfig = mDisplayDeviceConfig.isAutoBrightnessAvailable();
+
if (!mUseSoftwareAutoBrightnessConfig) {
return;
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 58a182a61e44..002209e32304 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -1390,17 +1390,17 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
@VisibleForTesting
- static class SurfaceControlProxy {
+ public static class SurfaceControlProxy {
public SurfaceControl.DynamicDisplayInfo getDynamicDisplayInfo(IBinder token) {
return SurfaceControl.getDynamicDisplayInfo(token);
}
public long[] getPhysicalDisplayIds() {
- return SurfaceControl.getPhysicalDisplayIds();
+ return DisplayControl.getPhysicalDisplayIds();
}
public IBinder getPhysicalDisplayToken(long physicalDisplayId) {
- return SurfaceControl.getPhysicalDisplayToken(physicalDisplayId);
+ return DisplayControl.getPhysicalDisplayToken(physicalDisplayId);
}
public SurfaceControl.StaticDisplayInfo getStaticDisplayInfo(IBinder displayToken) {
diff --git a/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessModeStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessModeStrategy.java
new file mode 100644
index 000000000000..3be5933cd3f1
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessModeStrategy.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 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.display.brightness.strategy;
+
+import android.hardware.display.DisplayManagerInternal;
+
+import com.android.server.display.DisplayBrightnessState;
+
+import java.io.PrintWriter;
+
+/**
+ * An interface to define the general skeleton of how a BrightnessModeStrategy should look like
+ * This is responsible for deciding the DisplayBrightnessState that the display should change to,
+ * not taking into account clamping that might be needed
+ */
+public interface DisplayBrightnessModeStrategy {
+ /**
+ * Decides the DisplayBrightnessState that the system should change to.
+ *
+ * @param displayPowerRequest The request to evaluate the updated brightness
+ * @param displayState The target displayState to which the system should
+ * change to after processing the request
+ * @param displayBrightnessStateBuilder The DisplayBrightnessStateBuilder, consisting of
+ * DisplayBrightnessState that have been constructed so far
+ */
+ DisplayBrightnessState.Builder updateBrightness(
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, int displayState,
+ DisplayBrightnessState.Builder displayBrightnessStateBuilder);
+
+ /**
+ * Used to dump the state.
+ *
+ * @param writer The PrintWriter used to dump the state.
+ */
+ void dump(PrintWriter writer);
+}
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 4e4f4544e068..b8af1bfcc254 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -118,7 +118,7 @@ final class DreamController {
public void startDream(Binder token, ComponentName name,
boolean isPreviewMode, boolean canDoze, int userId, PowerManager.WakeLock wakeLock,
- ComponentName overlayComponentName) {
+ ComponentName overlayComponentName, String reason) {
stopDream(true /*immediate*/, "starting new dream");
Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream");
@@ -128,7 +128,7 @@ final class DreamController {
Slog.i(TAG, "Starting dream: name=" + name
+ ", isPreviewMode=" + isPreviewMode + ", canDoze=" + canDoze
- + ", userId=" + userId);
+ + ", userId=" + userId + ", reason='" + reason + "'");
mCurrentDream = new DreamRecord(token, name, isPreviewMode, canDoze, userId, wakeLock);
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index e1b18f2226b8..2f18e78b8580 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -59,6 +59,7 @@ import android.util.Slog;
import android.view.Display;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.util.DumpUtils;
@@ -84,6 +85,9 @@ public final class DreamManagerService extends SystemService {
private static final boolean DEBUG = false;
private static final String TAG = "DreamManagerService";
+ private static final String DOZE_WAKE_LOCK_TAG = "dream:doze";
+ private static final String DREAM_WAKE_LOCK_TAG = "dream:dream";
+
private final Object mLock = new Object();
private final Context mContext;
@@ -98,17 +102,11 @@ public final class DreamManagerService extends SystemService {
private final ComponentName mAmbientDisplayComponent;
private final boolean mDismissDreamOnActivityStart;
- private Binder mCurrentDreamToken;
- private ComponentName mCurrentDreamName;
- private int mCurrentDreamUserId;
- private boolean mCurrentDreamIsPreview;
- private boolean mCurrentDreamCanDoze;
- private boolean mCurrentDreamIsDozing;
- private boolean mCurrentDreamIsWaking;
+ @GuardedBy("mLock")
+ private DreamRecord mCurrentDream;
+
private boolean mForceAmbientDisplayEnabled;
- private boolean mDreamsOnlyEnabledForSystemUser;
- private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
- private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+ private final boolean mDreamsOnlyEnabledForSystemUser;
// A temporary dream component that, when present, takes precedence over user configured dream
// component.
@@ -116,7 +114,7 @@ public final class DreamManagerService extends SystemService {
private ComponentName mDreamOverlayServiceName;
- private AmbientDisplayConfiguration mDozeConfig;
+ private final AmbientDisplayConfiguration mDozeConfig;
private final ActivityInterceptorCallback mActivityInterceptorCallback =
new ActivityInterceptorCallback() {
@Nullable
@@ -132,8 +130,14 @@ public final class DreamManagerService extends SystemService {
final boolean activityAllowed = activityType == ACTIVITY_TYPE_HOME
|| activityType == ACTIVITY_TYPE_DREAM
|| activityType == ACTIVITY_TYPE_ASSISTANT;
- if (mCurrentDreamToken != null && !mCurrentDreamIsWaking
- && !mCurrentDreamIsDozing && !activityAllowed) {
+
+ boolean shouldRequestAwaken;
+ synchronized (mLock) {
+ shouldRequestAwaken = mCurrentDream != null && !mCurrentDream.isWaking
+ && !mCurrentDream.isDozing && !activityAllowed;
+ }
+
+ if (shouldRequestAwaken) {
requestAwakenInternal(
"stopping dream due to activity start: " + activityInfo.name);
}
@@ -149,7 +153,7 @@ public final class DreamManagerService extends SystemService {
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
mAtmInternal = getLocalService(ActivityTaskManagerInternal.class);
- mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG);
+ mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, DOZE_WAKE_LOCK_TAG);
mDozeConfig = new AmbientDisplayConfiguration(mContext);
mUiEventLogger = new UiEventLoggerImpl();
mDreamUiEventLogger = new DreamUiEventLoggerImpl(
@@ -197,43 +201,38 @@ public final class DreamManagerService extends SystemService {
}
private void dumpInternal(PrintWriter pw) {
- pw.println("DREAM MANAGER (dumpsys dreams)");
- pw.println();
- pw.println("mCurrentDreamToken=" + mCurrentDreamToken);
- pw.println("mCurrentDreamName=" + mCurrentDreamName);
- pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId);
- pw.println("mCurrentDreamIsPreview=" + mCurrentDreamIsPreview);
- pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze);
- pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing);
- pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking);
- pw.println("mForceAmbientDisplayEnabled=" + mForceAmbientDisplayEnabled);
- pw.println("mDreamsOnlyEnabledForSystemUser=" + mDreamsOnlyEnabledForSystemUser);
- pw.println("mCurrentDreamDozeScreenState="
- + Display.stateToString(mCurrentDreamDozeScreenState));
- pw.println("mCurrentDreamDozeScreenBrightness=" + mCurrentDreamDozeScreenBrightness);
- pw.println("getDozeComponent()=" + getDozeComponent());
- pw.println();
-
- DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() {
- @Override
- public void dump(PrintWriter pw, String prefix) {
- mController.dump(pw);
- }
- }, pw, "", 200);
+ synchronized (mLock) {
+ pw.println("DREAM MANAGER (dumpsys dreams)");
+ pw.println();
+ pw.println("mCurrentDream=" + mCurrentDream);
+ pw.println("mForceAmbientDisplayEnabled=" + mForceAmbientDisplayEnabled);
+ pw.println("mDreamsOnlyEnabledForSystemUser=" + mDreamsOnlyEnabledForSystemUser);
+ pw.println("getDozeComponent()=" + getDozeComponent());
+ pw.println();
+
+ DumpUtils.dumpAsync(mHandler, (pw1, prefix) -> mController.dump(pw1), pw, "", 200);
+ }
}
/** Whether a real dream is occurring. */
private boolean isDreamingInternal() {
synchronized (mLock) {
- return mCurrentDreamToken != null && !mCurrentDreamIsPreview
- && !mCurrentDreamIsWaking;
+ return mCurrentDream != null && !mCurrentDream.isPreview
+ && !mCurrentDream.isWaking;
+ }
+ }
+
+ /** Whether a doze is occurring. */
+ private boolean isDozingInternal() {
+ synchronized (mLock) {
+ return mCurrentDream != null && mCurrentDream.isDozing;
}
}
/** Whether a real dream, or a dream preview is occurring. */
private boolean isDreamingOrInPreviewInternal() {
synchronized (mLock) {
- return mCurrentDreamToken != null && !mCurrentDreamIsWaking;
+ return mCurrentDream != null && !mCurrentDream.isWaking;
}
}
@@ -273,7 +272,7 @@ public final class DreamManagerService extends SystemService {
// locks are held and the user activity timeout has expired then the
// device may simply go to sleep.
synchronized (mLock) {
- if (mCurrentDreamToken == token) {
+ if (mCurrentDream != null && mCurrentDream.token == token) {
stopDreamLocked(immediate, "finished self");
}
}
@@ -281,16 +280,17 @@ public final class DreamManagerService extends SystemService {
private void testDreamInternal(ComponentName dream, int userId) {
synchronized (mLock) {
- startDreamLocked(dream, true /*isPreviewMode*/, false /*canDoze*/, userId);
+ startDreamLocked(dream, true /*isPreviewMode*/, false /*canDoze*/, userId,
+ "test dream" /*reason*/);
}
}
- private void startDreamInternal(boolean doze) {
+ private void startDreamInternal(boolean doze, String reason) {
final int userId = ActivityManager.getCurrentUser();
final ComponentName dream = chooseDreamForUser(doze, userId);
if (dream != null) {
synchronized (mLock) {
- startDreamLocked(dream, false /*isPreviewMode*/, doze, userId);
+ startDreamLocked(dream, false /*isPreviewMode*/, doze, userId, reason);
}
}
}
@@ -314,13 +314,13 @@ public final class DreamManagerService extends SystemService {
}
synchronized (mLock) {
- if (mCurrentDreamToken == token && mCurrentDreamCanDoze) {
- mCurrentDreamDozeScreenState = screenState;
- mCurrentDreamDozeScreenBrightness = screenBrightness;
+ if (mCurrentDream != null && mCurrentDream.token == token && mCurrentDream.canDoze) {
+ mCurrentDream.dozeScreenState = screenState;
+ mCurrentDream.dozeScreenBrightness = screenBrightness;
mPowerManagerInternal.setDozeOverrideFromDreamManager(
screenState, screenBrightness);
- if (!mCurrentDreamIsDozing) {
- mCurrentDreamIsDozing = true;
+ if (!mCurrentDream.isDozing) {
+ mCurrentDream.isDozing = true;
mDozeWakeLock.acquire();
}
}
@@ -333,8 +333,8 @@ public final class DreamManagerService extends SystemService {
}
synchronized (mLock) {
- if (mCurrentDreamToken == token && mCurrentDreamIsDozing) {
- mCurrentDreamIsDozing = false;
+ if (mCurrentDream != null && mCurrentDream.token == token && mCurrentDream.isDozing) {
+ mCurrentDream.isDozing = false;
mDozeWakeLock.release();
mPowerManagerInternal.setDozeOverrideFromDreamManager(
Display.STATE_UNKNOWN, PowerManager.BRIGHTNESS_DEFAULT);
@@ -403,7 +403,7 @@ public final class DreamManagerService extends SystemService {
ComponentName[] components = componentsFromString(names);
// first, ensure components point to valid services
- List<ComponentName> validComponents = new ArrayList<ComponentName>();
+ List<ComponentName> validComponents = new ArrayList<>();
if (components != null) {
for (ComponentName component : components) {
if (validateDream(component)) {
@@ -439,8 +439,9 @@ public final class DreamManagerService extends SystemService {
mSystemDreamComponent = componentName;
// Switch dream if currently dreaming and not dozing.
- if (isDreamingInternal() && !mCurrentDreamIsDozing) {
- startDreamInternal(false);
+ if (isDreamingInternal() && !isDozingInternal()) {
+ startDreamInternal(false /*doze*/, (mSystemDreamComponent == null ? "clear" : "set")
+ + " system dream component" /*reason*/);
}
}
}
@@ -478,13 +479,16 @@ public final class DreamManagerService extends SystemService {
}
}
+ @GuardedBy("mLock")
private void startDreamLocked(final ComponentName name,
- final boolean isPreviewMode, final boolean canDoze, final int userId) {
- if (!mCurrentDreamIsWaking
- && Objects.equals(mCurrentDreamName, name)
- && mCurrentDreamIsPreview == isPreviewMode
- && mCurrentDreamCanDoze == canDoze
- && mCurrentDreamUserId == userId) {
+ final boolean isPreviewMode, final boolean canDoze, final int userId,
+ final String reason) {
+ if (mCurrentDream != null
+ && !mCurrentDream.isWaking
+ && Objects.equals(mCurrentDream.name, name)
+ && mCurrentDream.isPreview == isPreviewMode
+ && mCurrentDream.canDoze == canDoze
+ && mCurrentDream.userId == userId) {
Slog.i(TAG, "Already in target dream.");
return;
}
@@ -493,73 +497,60 @@ public final class DreamManagerService extends SystemService {
Slog.i(TAG, "Entering dreamland.");
- final Binder newToken = new Binder();
- mCurrentDreamToken = newToken;
- mCurrentDreamName = name;
- mCurrentDreamIsPreview = isPreviewMode;
- mCurrentDreamCanDoze = canDoze;
- mCurrentDreamUserId = userId;
+ mCurrentDream = new DreamRecord(name, userId, isPreviewMode, canDoze);
- if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) {
+ if (!mCurrentDream.name.equals(mAmbientDisplayComponent)) {
// TODO(b/213906448): Remove when metrics based on new atom are fully rolled out.
mUiEventLogger.log(DreamUiEventLogger.DreamUiEventEnum.DREAM_START);
mDreamUiEventLogger.log(DreamUiEventLogger.DreamUiEventEnum.DREAM_START,
- mCurrentDreamName.flattenToString());
+ mCurrentDream.name.flattenToString());
}
PowerManager.WakeLock wakeLock = mPowerManager
- .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream");
+ .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, DREAM_WAKE_LOCK_TAG);
+ final Binder dreamToken = mCurrentDream.token;
mHandler.post(wakeLock.wrap(() -> {
mAtmInternal.notifyDreamStateChanged(true);
- mController.startDream(newToken, name, isPreviewMode, canDoze, userId, wakeLock,
- mDreamOverlayServiceName);
+ mController.startDream(dreamToken, name, isPreviewMode, canDoze, userId, wakeLock,
+ mDreamOverlayServiceName, reason);
}));
}
+ @GuardedBy("mLock")
private void stopDreamLocked(final boolean immediate, String reason) {
- if (mCurrentDreamToken != null) {
+ if (mCurrentDream != null) {
if (immediate) {
Slog.i(TAG, "Leaving dreamland.");
cleanupDreamLocked();
- } else if (mCurrentDreamIsWaking) {
+ } else if (mCurrentDream.isWaking) {
return; // already waking
} else {
Slog.i(TAG, "Gently waking up from dream.");
- mCurrentDreamIsWaking = true;
+ mCurrentDream.isWaking = true;
}
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- Slog.i(TAG, "Performing gentle wake from dream.");
- mController.stopDream(immediate, reason);
- }
- });
+ mHandler.post(() -> mController.stopDream(immediate, reason));
}
}
+ @GuardedBy("mLock")
private void cleanupDreamLocked() {
- if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) {
+ mHandler.post(() -> mAtmInternal.notifyDreamStateChanged(false /*dreaming*/));
+
+ if (mCurrentDream == null) {
+ return;
+ }
+
+ if (!mCurrentDream.name.equals(mAmbientDisplayComponent)) {
// TODO(b/213906448): Remove when metrics based on new atom are fully rolled out.
mUiEventLogger.log(DreamUiEventLogger.DreamUiEventEnum.DREAM_STOP);
mDreamUiEventLogger.log(DreamUiEventLogger.DreamUiEventEnum.DREAM_STOP,
- mCurrentDreamName.flattenToString());
- }
- mCurrentDreamToken = null;
- mCurrentDreamName = null;
- mCurrentDreamIsPreview = false;
- mCurrentDreamCanDoze = false;
- mCurrentDreamUserId = 0;
- mCurrentDreamIsWaking = false;
- if (mCurrentDreamIsDozing) {
- mCurrentDreamIsDozing = false;
+ mCurrentDream.name.flattenToString());
+ }
+ if (mCurrentDream.isDozing) {
mDozeWakeLock.release();
}
- mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
- mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
- mHandler.post(() -> {
- mAtmInternal.notifyDreamStateChanged(false);
- });
+ mCurrentDream = null;
}
private void checkPermission(String permission) {
@@ -606,7 +597,7 @@ public final class DreamManagerService extends SystemService {
@Override
public void onDreamStopped(Binder token) {
synchronized (mLock) {
- if (mCurrentDreamToken == token) {
+ if (mCurrentDream != null && mCurrentDream.token == token) {
cleanupDreamLocked();
}
}
@@ -624,7 +615,7 @@ public final class DreamManagerService extends SystemService {
* Handler for asynchronous operations performed by the dream manager.
* Ensures operations to {@link DreamController} are single-threaded.
*/
- private final class DreamHandler extends Handler {
+ private static final class DreamHandler extends Handler {
public DreamHandler(Looper looper) {
super(looper, null, true /*async*/);
}
@@ -865,13 +856,13 @@ public final class DreamManagerService extends SystemService {
private final class LocalService extends DreamManagerInternal {
@Override
- public void startDream(boolean doze) {
- startDreamInternal(doze);
+ public void startDream(boolean doze, String reason) {
+ startDreamInternal(doze, reason);
}
@Override
- public void stopDream(boolean immediate) {
- stopDreamInternal(immediate, "requested stopDream");
+ public void stopDream(boolean immediate, String reason) {
+ stopDreamInternal(immediate, reason);
}
@Override
@@ -890,13 +881,47 @@ public final class DreamManagerService extends SystemService {
}
}
+ private static final class DreamRecord {
+ public final Binder token = new Binder();
+ public final ComponentName name;
+ public final int userId;
+ public final boolean isPreview;
+ public final boolean canDoze;
+ public boolean isDozing = false;
+ public boolean isWaking = false;
+ public int dozeScreenState = Display.STATE_UNKNOWN;
+ public int dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+
+ DreamRecord(ComponentName name, int userId, boolean isPreview, boolean canDoze) {
+ this.name = name;
+ this.userId = userId;
+ this.isPreview = isPreview;
+ this.canDoze = canDoze;
+ }
+
+ @Override
+ public String toString() {
+ return "DreamRecord{"
+ + "token=" + token
+ + ", name=" + name
+ + ", userId=" + userId
+ + ", isPreview=" + isPreview
+ + ", canDoze=" + canDoze
+ + ", isDozing=" + isDozing
+ + ", isWaking=" + isWaking
+ + ", dozeScreenState=" + dozeScreenState
+ + ", dozeScreenBrightness=" + dozeScreenBrightness
+ + '}';
+ }
+ }
+
private final Runnable mSystemPropertiesChanged = new Runnable() {
@Override
public void run() {
if (DEBUG) Slog.d(TAG, "System properties changed");
synchronized (mLock) {
- if (mCurrentDreamName != null && mCurrentDreamCanDoze
- && !mCurrentDreamName.equals(getDozeComponent())) {
+ if (mCurrentDream != null && mCurrentDream.name != null && mCurrentDream.canDoze
+ && !mCurrentDream.name.equals(getDozeComponent())) {
// May have updated the doze component, wake up
mPowerManager.wakeUp(SystemClock.uptimeMillis(),
"android.server.dreams:SYSPROP");
diff --git a/services/core/java/com/android/server/input/BatteryController.java b/services/core/java/com/android/server/input/BatteryController.java
index 696b6047137a..324eefc809e8 100644
--- a/services/core/java/com/android/server/input/BatteryController.java
+++ b/services/core/java/com/android/server/input/BatteryController.java
@@ -38,7 +38,7 @@ import android.view.InputDevice;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.input.BatteryController.UEventManager.UEventListener;
+import com.android.server.input.BatteryController.UEventManager.UEventBatteryListener;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -438,7 +438,7 @@ final class BatteryController {
private State mState;
@Nullable
- private UEventListener mUEventListener;
+ private UEventBatteryListener mUEventBatteryListener;
DeviceMonitor(int deviceId) {
mState = new State(deviceId);
@@ -473,20 +473,26 @@ final class BatteryController {
return;
}
final int deviceId = mState.deviceId;
- mUEventListener = new UEventListener() {
+ mUEventBatteryListener = new UEventBatteryListener() {
@Override
- public void onUEvent(long eventTime) {
+ public void onBatteryUEvent(long eventTime) {
handleUEventNotification(deviceId, eventTime);
}
};
- mUEventManager.addListener(mUEventListener, "DEVPATH=" + batteryPath);
+ mUEventManager.addListener(
+ mUEventBatteryListener, "DEVPATH=" + formatDevPath(batteryPath));
+ }
+
+ private String formatDevPath(String path) {
+ // Remove the "/sys" prefix if it has one.
+ return path.startsWith("/sys") ? path.substring(4) : path;
}
// This must be called when the device is no longer being monitored.
public void stopMonitoring() {
- if (mUEventListener != null) {
- mUEventManager.removeListener(mUEventListener);
- mUEventListener = null;
+ if (mUEventBatteryListener != null) {
+ mUEventManager.removeListener(mUEventBatteryListener);
+ mUEventBatteryListener = null;
}
}
@@ -498,7 +504,7 @@ final class BatteryController {
@Override
public String toString() {
return "state=" + mState
- + ", uEventListener=" + (mUEventListener != null ? "added" : "none");
+ + ", uEventListener=" + (mUEventBatteryListener != null ? "added" : "none");
}
}
@@ -507,7 +513,7 @@ final class BatteryController {
interface UEventManager {
@VisibleForTesting
- abstract class UEventListener {
+ abstract class UEventBatteryListener {
private final UEventObserver mObserver = new UEventObserver() {
@Override
public void onUEvent(UEvent event) {
@@ -517,18 +523,23 @@ final class BatteryController {
"UEventListener: Received UEvent: "
+ event + " eventTime: " + eventTime);
}
- UEventListener.this.onUEvent(eventTime);
+ if (!"CHANGE".equalsIgnoreCase(event.get("ACTION"))
+ || !"POWER_SUPPLY".equalsIgnoreCase(event.get("SUBSYSTEM"))) {
+ // Disregard any UEvents that do not correspond to battery changes.
+ return;
+ }
+ UEventBatteryListener.this.onBatteryUEvent(eventTime);
}
};
- public abstract void onUEvent(long eventTime);
+ public abstract void onBatteryUEvent(long eventTime);
}
- default void addListener(UEventListener listener, String match) {
+ default void addListener(UEventBatteryListener listener, String match) {
listener.mObserver.startObserving(match);
}
- default void removeListener(UEventListener listener) {
+ default void removeListener(UEventBatteryListener listener) {
listener.mObserver.stopObserving();
}
}
diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java
index 7dce28c91072..5513cd62a5c5 100644
--- a/services/core/java/com/android/server/input/PersistentDataStore.java
+++ b/services/core/java/com/android/server/input/PersistentDataStore.java
@@ -556,11 +556,10 @@ final class PersistentDataStore {
}
for (int i = 0; i < mKeyboardBacklightBrightnessMap.size(); i++) {
- int lightId = mKeyboardBacklightBrightnessMap.valueAt(i);
serializer.startTag(null, "light-info");
- serializer.attributeInt(null, "light-id", lightId);
+ serializer.attributeInt(null, "light-id", mKeyboardBacklightBrightnessMap.keyAt(i));
serializer.attributeInt(null, "light-brightness",
- mKeyboardBacklightBrightnessMap.get(lightId));
+ mKeyboardBacklightBrightnessMap.valueAt(i));
serializer.endTag(null, "light-info");
}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index 82436cc0890a..1a0f6f7fff4a 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -284,4 +284,13 @@ final class IInputMethodInvoker {
logRemoteException(e);
}
}
+
+ @AnyThread
+ void setStylusWindowIdleTimeoutForTest(long timeout) {
+ try {
+ mTarget.setStylusWindowIdleTimeoutForTest(timeout);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e075a4edf705..2be84d059286 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -60,6 +60,7 @@ import android.annotation.AnyThread;
import android.annotation.BinderThread;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.DurationMillisLong;
import android.annotation.EnforcePermission;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -299,7 +300,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private final DisplayManagerInternal mDisplayManagerInternal;
private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
new ArrayMap<>();
- private final UserManager mUserManager;
private final UserManagerInternal mUserManagerInternal;
private final InputMethodMenuController mMenuController;
@NonNull private final InputMethodBindingController mBindingController;
@@ -1721,7 +1721,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mInputMethodDeviceConfigs = new InputMethodDeviceConfigs();
mImeDisplayValidator = mWindowManagerInternal::getDisplayImePolicy;
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
- mUserManager = mContext.getSystemService(UserManager.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
@@ -1912,7 +1911,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
void updateCurrentProfileIds() {
mSettings.setCurrentProfileIds(
- mUserManager.getProfileIdsWithDisabled(mSettings.getCurrentUserId()));
+ mUserManagerInternal.getProfileIds(mSettings.getCurrentUserId(),
+ false /* enabledOnly */));
}
@Override
@@ -2036,9 +2036,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (resolvedUserIds.length != 1) {
return Collections.emptyList();
}
+ final int callingUid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- return getInputMethodListLocked(resolvedUserIds[0], directBootAwareness);
+ return getInputMethodListLocked(
+ resolvedUserIds[0], directBootAwareness, callingUid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2057,9 +2059,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (resolvedUserIds.length != 1) {
return Collections.emptyList();
}
+ final int callingUid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- return getEnabledInputMethodListLocked(resolvedUserIds[0]);
+ return getEnabledInputMethodListLocked(resolvedUserIds[0], callingUid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2090,12 +2093,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
private List<InputMethodInfo> getInputMethodListLocked(@UserIdInt int userId,
- @DirectBootAwareness int directBootAwareness) {
+ @DirectBootAwareness int directBootAwareness, int callingUid) {
final ArrayList<InputMethodInfo> methodList;
+ final InputMethodSettings settings;
if (userId == mSettings.getCurrentUserId()
&& directBootAwareness == DirectBootAwareness.AUTO) {
// Create a copy.
methodList = new ArrayList<>(mMethodList);
+ settings = mSettings;
} else {
final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
methodList = new ArrayList<>();
@@ -2104,19 +2109,31 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
methodList, directBootAwareness);
+ settings = new InputMethodSettings(mContext, methodMap, userId, true /* copyOnWrite */);
}
+ // filter caller's access to input methods
+ methodList.removeIf(imi ->
+ !canCallerAccessInputMethod(imi.getPackageName(), callingUid, userId, settings));
return methodList;
}
@GuardedBy("ImfLock.class")
- private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId) {
+ private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId,
+ int callingUid) {
+ final ArrayList<InputMethodInfo> methodList;
+ final InputMethodSettings settings;
if (userId == mSettings.getCurrentUserId()) {
- return mSettings.getEnabledInputMethodListLocked();
+ methodList = mSettings.getEnabledInputMethodListLocked();
+ settings = mSettings;
+ } else {
+ final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
+ settings = new InputMethodSettings(mContext, methodMap, userId, true /* copyOnWrite */);
+ methodList = settings.getEnabledInputMethodListLocked();
}
- final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
- final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, userId,
- true);
- return settings.getEnabledInputMethodListLocked();
+ // filter caller's access to input methods
+ methodList.removeIf(imi ->
+ !canCallerAccessInputMethod(imi.getPackageName(), callingUid, userId, settings));
+ return methodList;
}
@GuardedBy("ImfLock.class")
@@ -2155,10 +2172,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
synchronized (ImfLock.class) {
+ final int callingUid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
return getEnabledInputMethodSubtypeListLocked(imiId,
- allowsImplicitlyEnabledSubtypes, userId);
+ allowsImplicitlyEnabledSubtypes, userId, callingUid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2167,7 +2185,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(String imiId,
- boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
+ boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId, int callingUid) {
if (userId == mSettings.getCurrentUserId()) {
final InputMethodInfo imi;
String selectedMethodId = getSelectedMethodIdLocked();
@@ -2176,7 +2194,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
} else {
imi = mMethodMap.get(imiId);
}
- if (imi == null) {
+ if (imi == null || !canCallerAccessInputMethod(
+ imi.getPackageName(), callingUid, userId, mSettings)) {
return Collections.emptyList();
}
return mSettings.getEnabledInputMethodSubtypeListLocked(
@@ -2189,6 +2208,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, userId,
true);
+ if (!canCallerAccessInputMethod(imi.getPackageName(), callingUid, userId, settings)) {
+ return Collections.emptyList();
+ }
return settings.getEnabledInputMethodSubtypeListLocked(
imi, allowsImplicitlyEnabledSubtypes);
}
@@ -3652,7 +3674,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
scheduleSwitchUserTaskLocked(userId, cs.mClient);
return InputBindResult.USER_SWITCHING;
}
- for (int profileId : mUserManager.getProfileIdsWithDisabled(nextUserId)) {
+ final int[] profileIdsWithDisabled = mUserManagerInternal.getProfileIds(
+ mSettings.getCurrentUserId(), false /* enabledOnly */);
+ for (int profileId : profileIdsWithDisabled) {
if (profileId == userId) {
scheduleSwitchUserTaskLocked(userId, cs.mClient);
return InputBindResult.USER_SWITCHING;
@@ -4467,6 +4491,31 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ /**
+ * Helper method to set a stylus idle-timeout after which handwriting {@code InkWindow}
+ * will be removed.
+ * @param timeout to set in milliseconds. To reset to default, use a value <= zero.
+ */
+ @BinderThread
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @Override
+ public void setStylusWindowIdleTimeoutForTest(
+ IInputMethodClient client, @DurationMillisLong long timeout) {
+ int uid = Binder.getCallingUid();
+ synchronized (ImfLock.class) {
+ if (!canInteractWithImeLocked(uid, client, "setStylusWindowIdleTimeoutForTest")) {
+ return;
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (DEBUG) Slog.v(TAG, "Setting stylus window idle timeout");
+ getCurMethodLocked().setStylusWindowIdleTimeoutForTest(timeout);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
@GuardedBy("ImfLock.class")
private void removeVirtualStylusIdForTestSessionLocked() {
removeStylusDeviceIdLocked(VIRTUAL_STYLUS_ID_FOR_TEST);
@@ -5411,6 +5460,34 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return true;
}
+ /**
+ * Filter the access to the input method by rules of the package visibility. Return {@code true}
+ * if the given input method is the currently selected one or visible to the caller.
+ *
+ * @param targetPkgName The package name of input method to check.
+ * @param callingUid The caller that is going to access the input method.
+ * @param userId The user ID where the input method resides.
+ * @param settings The input method settings under the given user ID.
+ * @return {@code true} if caller is able to access the input method.
+ */
+ private boolean canCallerAccessInputMethod(@NonNull String targetPkgName, int callingUid,
+ @UserIdInt int userId, @NonNull InputMethodSettings settings) {
+ final String methodId = settings.getSelectedInputMethod();
+ final ComponentName selectedInputMethod = methodId != null
+ ? InputMethodUtils.convertIdToComponentName(methodId) : null;
+ if (selectedInputMethod != null
+ && selectedInputMethod.getPackageName().equals(targetPkgName)) {
+ return true;
+ }
+ final boolean canAccess = !mPackageManagerInternal.filterAppAccess(
+ targetPkgName, callingUid, userId);
+ if (DEBUG && !canAccess) {
+ Slog.d(TAG, "Input method " + targetPkgName
+ + " is not visible to the caller " + callingUid);
+ }
+ return canAccess;
+ }
+
private void publishLocalService() {
LocalServices.addService(InputMethodManagerInternal.class, new LocalServiceImpl());
}
@@ -5432,14 +5509,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
synchronized (ImfLock.class) {
- return getInputMethodListLocked(userId, DirectBootAwareness.AUTO);
+ return getInputMethodListLocked(userId, DirectBootAwareness.AUTO,
+ Process.SYSTEM_UID);
}
}
@Override
public List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
synchronized (ImfLock.class) {
- return getEnabledInputMethodListLocked(userId);
+ return getEnabledInputMethodListLocked(userId, Process.SYSTEM_UID);
}
}
@@ -6069,8 +6147,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
try (PrintWriter pr = shellCommand.getOutPrintWriter()) {
for (int userId : userIds) {
final List<InputMethodInfo> methods = all
- ? getInputMethodListLocked(userId, DirectBootAwareness.AUTO)
- : getEnabledInputMethodListLocked(userId);
+ ? getInputMethodListLocked(
+ userId, DirectBootAwareness.AUTO, Process.SHELL_UID)
+ : getEnabledInputMethodListLocked(userId, Process.SHELL_UID);
if (userIds.length > 1) {
pr.print("User #");
pr.print(userId);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 69b06613b298..c7ff8caf176b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -20,11 +20,13 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Build;
import android.os.UserHandle;
@@ -645,13 +647,11 @@ final class InputMethodUtils {
if (imi != null && imi.getSubtypeCount() > 0) {
List<InputMethodSubtype> implicitlyEnabledSubtypes =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(mRes, imi);
- if (implicitlyEnabledSubtypes != null) {
- final int numSubtypes = implicitlyEnabledSubtypes.size();
- for (int i = 0; i < numSubtypes; ++i) {
- final InputMethodSubtype st = implicitlyEnabledSubtypes.get(i);
- if (String.valueOf(st.hashCode()).equals(subtypeHashCode)) {
- return subtypeHashCode;
- }
+ final int numSubtypes = implicitlyEnabledSubtypes.size();
+ for (int i = 0; i < numSubtypes; ++i) {
+ final InputMethodSubtype st = implicitlyEnabledSubtypes.get(i);
+ if (String.valueOf(st.hashCode()).equals(subtypeHashCode)) {
+ return subtypeHashCode;
}
}
}
@@ -1000,4 +1000,16 @@ final class InputMethodUtils {
}
return new int[]{sourceUserId};
}
+
+ /**
+ * Convert the input method ID to a component name
+ *
+ * @param id A unique ID for this input method.
+ * @return The component name of the input method.
+ * @see InputMethodInfo#computeId(ResolveInfo)
+ */
+ @Nullable
+ public static ComponentName convertIdToComponentName(@NonNull String id) {
+ return ComponentName.unflattenFromString(id);
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
index f07539fa5eb2..f49fa6ecca4a 100644
--- a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
+++ b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
@@ -16,6 +16,7 @@
package com.android.server.inputmethod;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources;
import android.os.LocaleList;
@@ -123,6 +124,7 @@ final class SubtypeUtils {
source -> source != null ? source.getLocaleObject() : null;
@VisibleForTesting
+ @NonNull
static ArrayList<InputMethodSubtype> getImplicitlyApplicableSubtypesLocked(
Resources res, InputMethodInfo imi) {
final LocaleList systemLocales = res.getConfiguration().getLocales();
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 66bdadb185c1..ab69c3426d04 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -333,16 +333,25 @@ public class SyntheticPasswordManager {
byte scryptLogP;
public int credentialType;
byte[] salt;
- // If Weaver is available, then this field is empty. Otherwise, it is the Gatekeeper
- // password handle that resulted from enrolling the hashed LSKF.
+ // This is the Gatekeeper password handle that resulted from enrolling the stretched LSKF,
+ // when applicable. This field isn't used if Weaver is available, or in new protectors when
+ // the LSKF is empty.
public byte[] passwordHandle;
- public static PasswordData create(int passwordType) {
+ public static PasswordData create(int credentialType) {
PasswordData result = new PasswordData();
- result.scryptLogN = PASSWORD_SCRYPT_LOG_N;
- result.scryptLogR = PASSWORD_SCRYPT_LOG_R;
- result.scryptLogP = PASSWORD_SCRYPT_LOG_P;
- result.credentialType = passwordType;
+ if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+ // When the LSKF is empty, scrypt provides no security benefit, so just use the
+ // minimum parameters (N=2, r=1, p=1).
+ result.scryptLogN = 1;
+ result.scryptLogR = 0;
+ result.scryptLogP = 0;
+ } else {
+ result.scryptLogN = PASSWORD_SCRYPT_LOG_N;
+ result.scryptLogR = PASSWORD_SCRYPT_LOG_R;
+ result.scryptLogP = PASSWORD_SCRYPT_LOG_P;
+ }
+ result.credentialType = credentialType;
result.salt = secureRandom(PASSWORD_SALT_LENGTH);
return result;
}
@@ -776,11 +785,12 @@ public class SyntheticPasswordManager {
long protectorId = generateProtectorId();
PasswordData pwd = PasswordData.create(credential.getType());
byte[] stretchedLskf = stretchLskf(credential, pwd);
- final long sid;
+ long sid = GateKeeper.INVALID_SECURE_USER_ID;
final byte[] protectorSecret;
if (isWeaverAvailable()) {
- // Protector uses Weaver to verify the LSKF
+ // Weaver is available, so make the protector use it to verify the LSKF. Do this even
+ // if the LSKF is empty, as that gives us support for securely deleting the protector.
int weaverSlot = getNextAvailableWeaverSlot();
Slog.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
byte[] weaverSecret = weaverEnroll(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf),
@@ -794,33 +804,34 @@ public class SyntheticPasswordManager {
// No need to pass in quality since the credential type already encodes sufficient info
synchronizeWeaverFrpPassword(pwd, 0, userId, weaverSlot);
- pwd.passwordHandle = null;
- sid = GateKeeper.INVALID_SECURE_USER_ID;
protectorSecret = transformUnderWeaverSecret(stretchedLskf, weaverSecret);
} else {
- // Protector uses Gatekeeper to verify the LSKF
-
- // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
- // to prevent them from accumulating and causing problems.
- try {
- gatekeeper.clearSecureUserId(fakeUserId(userId));
- } catch (RemoteException ignore) {
- Slog.w(TAG, "Failed to clear SID from gatekeeper");
- }
- GateKeeperResponse response;
- try {
- response = gatekeeper.enroll(fakeUserId(userId), null, null,
- stretchedLskfToGkPassword(stretchedLskf));
- } catch (RemoteException e) {
- throw new IllegalStateException("Failed to enroll LSKF for new SP protector for "
- + "user " + userId, e);
- }
- if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
- throw new IllegalStateException("Failed to enroll LSKF for new SP protector for "
- + "user " + userId);
+ // Weaver is unavailable, so make the protector use Gatekeeper to verify the LSKF
+ // instead. However, skip Gatekeeper when the LSKF is empty, since it wouldn't give any
+ // benefit in that case as Gatekeeper isn't expected to provide secure deletion.
+ if (!credential.isNone()) {
+ // In case GK enrollment leaves persistent state around (in RPMB), this will nuke
+ // them to prevent them from accumulating and causing problems.
+ try {
+ gatekeeper.clearSecureUserId(fakeUserId(userId));
+ } catch (RemoteException ignore) {
+ Slog.w(TAG, "Failed to clear SID from gatekeeper");
+ }
+ GateKeeperResponse response;
+ try {
+ response = gatekeeper.enroll(fakeUserId(userId), null, null,
+ stretchedLskfToGkPassword(stretchedLskf));
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed to enroll LSKF for new SP protector"
+ + " for user " + userId, e);
+ }
+ if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
+ throw new IllegalStateException("Failed to enroll LSKF for new SP protector"
+ + " for user " + userId);
+ }
+ pwd.passwordHandle = response.getPayload();
+ sid = sidFromPasswordHandle(pwd.passwordHandle);
}
- pwd.passwordHandle = response.getPayload();
- sid = sidFromPasswordHandle(pwd.passwordHandle);
protectorSecret = transformUnderSecdiscardable(stretchedLskf,
createSecdiscardable(protectorId, userId));
// No need to pass in quality since the credential type already encodes sufficient info
@@ -1049,7 +1060,7 @@ public class SyntheticPasswordManager {
byte[] stretchedLskf = stretchLskf(credential, pwd);
final byte[] protectorSecret;
- final long sid;
+ long sid = GateKeeper.INVALID_SECURE_USER_ID;
int weaverSlot = loadWeaverSlot(protectorId, userId);
if (weaverSlot != INVALID_WEAVER_SLOT) {
// Protector uses Weaver to verify the LSKF
@@ -1062,54 +1073,62 @@ public class SyntheticPasswordManager {
if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
return result;
}
- sid = GateKeeper.INVALID_SECURE_USER_ID;
protectorSecret = transformUnderWeaverSecret(stretchedLskf,
result.gkResponse.getGatekeeperHAT());
} else {
- // Protector uses Gatekeeper to verify the LSKF
- byte[] gkPassword = stretchedLskfToGkPassword(stretchedLskf);
- GateKeeperResponse response;
- try {
- response = gatekeeper.verifyChallenge(fakeUserId(userId), 0L,
- pwd.passwordHandle, gkPassword);
- } catch (RemoteException e) {
- Slog.e(TAG, "gatekeeper verify failed", e);
- result.gkResponse = VerifyCredentialResponse.ERROR;
- return result;
- }
- int responseCode = response.getResponseCode();
- if (responseCode == GateKeeperResponse.RESPONSE_OK) {
- result.gkResponse = VerifyCredentialResponse.OK;
- if (response.getShouldReEnroll()) {
- GateKeeperResponse reenrollResponse;
- try {
- reenrollResponse = gatekeeper.enroll(fakeUserId(userId),
- pwd.passwordHandle, gkPassword, gkPassword);
- } catch (RemoteException e) {
- Slog.w(TAG, "Fail to invoke gatekeeper.enroll", e);
- reenrollResponse = GateKeeperResponse.ERROR;
- // continue the flow anyway
- }
- if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
- pwd.passwordHandle = reenrollResponse.getPayload();
- // Use the reenrollment opportunity to update credential type
- // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN)
- pwd.credentialType = credential.getType();
- saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId);
- synchronizeFrpPassword(pwd, 0, userId);
- } else {
- Slog.w(TAG, "Fail to re-enroll user password for user " + userId);
- // continue the flow anyway
+ // Weaver is unavailable, so the protector uses Gatekeeper to verify the LSKF, unless
+ // the LSKF is empty in which case Gatekeeper might not have been used at all.
+ if (pwd.passwordHandle == null) {
+ if (!credential.isNone()) {
+ Slog.e(TAG, "Missing Gatekeeper password handle for nonempty LSKF");
+ result.gkResponse = VerifyCredentialResponse.ERROR;
+ return result;
+ }
+ } else {
+ byte[] gkPassword = stretchedLskfToGkPassword(stretchedLskf);
+ GateKeeperResponse response;
+ try {
+ response = gatekeeper.verifyChallenge(fakeUserId(userId), 0L,
+ pwd.passwordHandle, gkPassword);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "gatekeeper verify failed", e);
+ result.gkResponse = VerifyCredentialResponse.ERROR;
+ return result;
+ }
+ int responseCode = response.getResponseCode();
+ if (responseCode == GateKeeperResponse.RESPONSE_OK) {
+ result.gkResponse = VerifyCredentialResponse.OK;
+ if (response.getShouldReEnroll()) {
+ GateKeeperResponse reenrollResponse;
+ try {
+ reenrollResponse = gatekeeper.enroll(fakeUserId(userId),
+ pwd.passwordHandle, gkPassword, gkPassword);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Fail to invoke gatekeeper.enroll", e);
+ reenrollResponse = GateKeeperResponse.ERROR;
+ // continue the flow anyway
+ }
+ if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
+ pwd.passwordHandle = reenrollResponse.getPayload();
+ // Use the reenrollment opportunity to update credential type
+ // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN)
+ pwd.credentialType = credential.getType();
+ saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId);
+ synchronizeFrpPassword(pwd, 0, userId);
+ } else {
+ Slog.w(TAG, "Fail to re-enroll user password for user " + userId);
+ // continue the flow anyway
+ }
}
+ } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
+ result.gkResponse = VerifyCredentialResponse.fromTimeout(response.getTimeout());
+ return result;
+ } else {
+ result.gkResponse = VerifyCredentialResponse.ERROR;
+ return result;
}
- } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
- result.gkResponse = VerifyCredentialResponse.fromTimeout(response.getTimeout());
- return result;
- } else {
- result.gkResponse = VerifyCredentialResponse.ERROR;
- return result;
+ sid = sidFromPasswordHandle(pwd.passwordHandle);
}
- sid = sidFromPasswordHandle(pwd.passwordHandle);
protectorSecret = transformUnderSecdiscardable(stretchedLskf,
loadSecdiscardable(protectorId, userId));
}
@@ -1463,7 +1482,8 @@ public class SyntheticPasswordManager {
return result;
}
- private int fakeUserId(int userId) {
+ @VisibleForTesting
+ static int fakeUserId(int userId) {
return 100000 + userId;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4b18add39999..77fea09b5ecc 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4872,6 +4872,13 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public int getHintsFromListenerNoToken() {
+ synchronized (mNotificationLock) {
+ return mListenerHints;
+ }
+ }
+
+ @Override
public void requestInterruptionFilterFromListener(INotificationListener token,
int interruptionFilter) throws RemoteException {
final long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 2789bcff0f68..ef1e11ccad55 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -177,11 +177,13 @@ public interface NotificationRecordLogger {
NOTIFICATION_CANCEL_USER_PEEK(190),
@UiEvent(doc = "Notification was canceled due to user dismissal from the always-on display")
NOTIFICATION_CANCEL_USER_AOD(191),
+ @UiEvent(doc = "Notification was canceled due to user dismissal from a bubble")
+ NOTIFICATION_CANCEL_USER_BUBBLE(1228),
+ @UiEvent(doc = "Notification was canceled due to user dismissal from the lockscreen")
+ NOTIFICATION_CANCEL_USER_LOCKSCREEN(193),
@UiEvent(doc = "Notification was canceled due to user dismissal from the notification"
+ " shade.")
NOTIFICATION_CANCEL_USER_SHADE(192),
- @UiEvent(doc = "Notification was canceled due to user dismissal from the lockscreen")
- NOTIFICATION_CANCEL_USER_LOCKSCREEN(193),
@UiEvent(doc = "Notification was canceled due to an assistant adjustment update.")
NOTIFICATION_CANCEL_ASSISTANT(906);
@@ -232,6 +234,10 @@ public interface NotificationRecordLogger {
return NOTIFICATION_CANCEL_USER_AOD;
case NotificationStats.DISMISSAL_SHADE:
return NOTIFICATION_CANCEL_USER_SHADE;
+ case NotificationStats.DISMISSAL_BUBBLE:
+ return NOTIFICATION_CANCEL_USER_BUBBLE;
+ case NotificationStats.DISMISSAL_LOCKSCREEN:
+ return NOTIFICATION_CANCEL_USER_LOCKSCREEN;
default:
if (NotificationManagerService.DBG) {
throw new IllegalArgumentException("Unexpected surface for user-dismiss "
diff --git a/services/core/java/com/android/server/pm/PackageManagerLocal.java b/services/core/java/com/android/server/pm/PackageManagerLocal.java
index c9b48bf3c2a1..21c2f2c9e330 100644
--- a/services/core/java/com/android/server/pm/PackageManagerLocal.java
+++ b/services/core/java/com/android/server/pm/PackageManagerLocal.java
@@ -95,8 +95,6 @@ public interface PackageManagerLocal {
* <p/>
* The snapshot assumes the caller is acting on behalf of the system and will not filter any
* results.
- *
- * @hide
*/
@NonNull
UnfilteredSnapshot withUnfilteredSnapshot();
@@ -106,7 +104,6 @@ public interface PackageManagerLocal {
* caller through {@link Binder#getCallingUid()} and {@link Binder#getCallingUserHandle()}.
*
* @see #withFilteredSnapshot(int, UserHandle)
- * @hide
*/
@NonNull
FilteredSnapshot withFilteredSnapshot();
@@ -121,7 +118,6 @@ public interface PackageManagerLocal {
* and permissions, including cross-user enforcement.
* @param user The user to query as, should usually be the user that the caller was
* invoked from.
- * @hide
*/
@SuppressWarnings("UserHandleName") // Ignore naming convention, not invoking action as user
@NonNull
@@ -130,6 +126,7 @@ public interface PackageManagerLocal {
/**
* @hide
*/
+ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
interface UnfilteredSnapshot extends AutoCloseable {
/**
@@ -160,6 +157,7 @@ public interface PackageManagerLocal {
/**
* @hide
*/
+ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
interface FilteredSnapshot extends AutoCloseable {
/**
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index b9770254fb46..56ec8e46291d 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -248,6 +248,18 @@ public abstract class UserManagerInternal {
boolean excludePreCreated);
/**
+ * Returns an array of ids for profiles associated with the specified user including the user
+ * itself.
+ * <p>Note that this includes all profile types (not including Restricted profiles).
+ *
+ * @param userId id of the user to return profiles for
+ * @param enabledOnly whether return only {@link UserInfo#isEnabled() enabled} profiles
+ * @return A non-empty array of ids of profiles associated with the specified user if the user
+ * exists. Otherwise, an empty array.
+ */
+ public abstract @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly);
+
+ /**
* Checks if the {@code callingUserId} and {@code targetUserId} are same or in same group
* and that the {@code callingUserId} is not a profile and {@code targetUserId} is enabled.
*
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0a89d131eda2..0a4669c0aace 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -6700,6 +6700,13 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
+ synchronized (mUsersLock) {
+ return getProfileIdsLU(userId, null /* userType */, enabledOnly).toArray();
+ }
+ }
+
+ @Override
public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
int state;
synchronized (mUserStates) {
diff --git a/services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java b/services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java
index 00c8f84b340b..4ff0d59aba7f 100644
--- a/services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java
+++ b/services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java
@@ -31,6 +31,7 @@ import com.android.server.pm.snapshot.PackageDataSnapshot;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -82,6 +83,7 @@ public class PackageManagerLocalImpl implements PackageManagerLocal {
mSnapshot = (Computer) snapshot;
}
+ @CallSuper
@Override
public void close() {
mClosed = true;
@@ -100,6 +102,9 @@ public class PackageManagerLocalImpl implements PackageManagerLocal {
private static class UnfilteredSnapshotImpl extends BaseSnapshotImpl implements
UnfilteredSnapshot {
+ @Nullable
+ private Map<String, PackageState> mCachedUnmodifiablePackageStates;
+
private UnfilteredSnapshotImpl(@NonNull PackageDataSnapshot snapshot) {
super(snapshot);
}
@@ -115,8 +120,17 @@ public class PackageManagerLocalImpl implements PackageManagerLocal {
public Map<String, PackageState> getPackageStates() {
checkClosed();
- //noinspection unchecked, RedundantCast
- return (Map<String, PackageState>) (Map<?, ?>) mSnapshot.getPackageStates();
+ if (mCachedUnmodifiablePackageStates == null) {
+ mCachedUnmodifiablePackageStates =
+ Collections.unmodifiableMap(mSnapshot.getPackageStates());
+ }
+ return mCachedUnmodifiablePackageStates;
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ mCachedUnmodifiablePackageStates = null;
}
}
@@ -152,6 +166,12 @@ public class PackageManagerLocalImpl implements PackageManagerLocal {
super.checkClosed();
}
+ @Override
+ public void close() {
+ super.close();
+ mFilteredPackageStates = null;
+ }
+
@Nullable
@Override
public PackageState getPackageState(@NonNull String packageName) {
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
index 5108fcd452f1..e3dad452c9f4 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -18,6 +18,7 @@ package com.android.server.pm.pkg;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -58,8 +59,27 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-/** @hide */
-//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+/**
+ * The representation of an application on disk, as parsed from its split APKs' manifests.
+ *
+ * Metadata available here is mostly device-state independent and indicates what the application
+ * author declared for their app.
+ *
+ * This is the system server in-process API equivalent of the public API {@link ApplicationInfo}.
+ * Note that because {@link ApplicationInfo} is stateful, several methods that exist on it may not
+ * be available here and need to be read through {@link PackageState} or {@link PackageUserState}.
+ *
+ * All instances of {@link AndroidPackage} are associated with a {@link PackageState}, and the
+ * only way to retrieve one is through {@link PackageState}. Note that the inverse does not apply
+ * and {@link AndroidPackage} may be null in several cases. See
+ * {@link PackageState#getAndroidPackage()}.
+ *
+ * The data available here is immutable and will throw {@link UnsupportedOperationException} if any
+ * collection type is mutated.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@Immutable
public interface AndroidPackage {
@@ -98,11 +118,13 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#FLAG_DEBUGGABLE
+ * @see R.styleable#AndroidManifestApplication_debuggable
*/
boolean isDebuggable();
/**
* @see ApplicationInfo#PRIVATE_FLAG_ISOLATED_SPLIT_LOADING
+ * @see R.styleable#AndroidManifest_isolatedSplits
*/
boolean isIsolatedSplitLoading();
@@ -113,16 +135,19 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#PRIVATE_FLAG_USE_EMBEDDED_DEX
+ * @see R.styleable#AndroidManifestApplication_useEmbeddedDex
*/
boolean isUseEmbeddedDex();
/**
* @see ApplicationInfo#PRIVATE_FLAG_USES_NON_SDK_API
+ * @see R.styleable#AndroidManifestApplication_usesNonSdkApi
*/
boolean isUsesNonSdkApi();
/**
* @see ApplicationInfo#FLAG_VM_SAFE_MODE
+ * @see R.styleable#AndroidManifestApplication_vmSafeMode
*/
boolean isVmSafeMode();
@@ -147,6 +172,7 @@ public interface AndroidPackage {
*
* @see ActivityInfo
* @see PackageInfo#activities
+ * @see R.styleable#AndroidManifestActivity
* @hide
*/
@Immutable.Ignore
@@ -179,7 +205,10 @@ public interface AndroidPackage {
@Nullable
String getAppComponentFactory();
- /** @hide */
+ /**
+ * @see R.styleable#AndroidManifestAttribution
+ * @hide
+ */
@Immutable.Ignore
@NonNull
List<ParsedAttribution> getAttributions();
@@ -188,6 +217,7 @@ public interface AndroidPackage {
* @see ApplicationInfo#AUTO_REVOKE_ALLOWED
* @see ApplicationInfo#AUTO_REVOKE_DISCOURAGED
* @see ApplicationInfo#AUTO_REVOKE_DISALLOWED
+ * @see R.styleable#AndroidManifestApplication_autoRevokePermissions
* @hide
*/
int getAutoRevokePermissions();
@@ -217,6 +247,7 @@ public interface AndroidPackage {
/**
* @see PackageInfo#baseRevisionCode
+ * @see R.styleable#AndroidManifest_revisionCode
* @hide
*/
int getBaseRevisionCode();
@@ -338,6 +369,7 @@ public interface AndroidPackage {
/**
* @see InstrumentationInfo
* @see PackageInfo#instrumentation
+ * @see R.styleable#AndroidManifestInstrumentation
* @hide
*/
@Immutable.Ignore
@@ -396,6 +428,8 @@ public interface AndroidPackage {
/**
* @see PackageInfo#getLongVersionCode()
+ * @see R.styleable#AndroidManifest_versionCode
+ * @see R.styleable#AndroidManifest_versionCodeMajor
* @hide
*/
long getLongVersionCode();
@@ -439,13 +473,17 @@ public interface AndroidPackage {
/**
* TODO(b/135203078): Make all the Bundles immutable (and non-null by shared empty reference?)
+ * @see R.styleable#AndroidManifestMetaData
* @hide
*/
@Immutable.Ignore
@Nullable
Bundle getMetaData();
- /** @hide */
+ /**
+ * @see R.attr#mimeGroup
+ * @hide
+ */
@Nullable
Set<String> getMimeGroups();
@@ -583,6 +621,7 @@ public interface AndroidPackage {
/**
* @see android.content.pm.PermissionGroupInfo
+ * @see R.styleable#AndroidManifestPermissionGroup
* @hide
*/
@Immutable.Ignore
@@ -619,6 +658,7 @@ public interface AndroidPackage {
/**
* @see android.content.pm.ProcessInfo
+ * @see R.styleable#AndroidManifestProcess
* @hide
*/
@Immutable.Ignore
@@ -627,6 +667,7 @@ public interface AndroidPackage {
/**
* Returns the properties set on the application
+ * @see R.styleable#AndroidManifestProperty
* @hide
*/
@Immutable.Ignore
@@ -653,6 +694,7 @@ public interface AndroidPackage {
*
* @see ProviderInfo
* @see PackageInfo#providers
+ * @see R.styleable#AndroidManifestProvider
* @hide
*/
@Immutable.Ignore
@@ -703,6 +745,7 @@ public interface AndroidPackage {
*
* @see ActivityInfo
* @see PackageInfo#receivers
+ * @see R.styleable#AndroidManifestReceiver
* @hide
*/
@Immutable.Ignore
@@ -751,6 +794,7 @@ public interface AndroidPackage {
*
* @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE
* @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE
+ * @see R.styleable#AndroidManifestApplication_resizeableActivity
* @hide
*/
@Nullable
@@ -807,6 +851,7 @@ public interface AndroidPackage {
*
* @see ServiceInfo
* @see PackageInfo#services
+ * @see R.styleable#AndroidManifestService
* @hide
*/
@Immutable.Ignore
@@ -1050,7 +1095,11 @@ public interface AndroidPackage {
@Nullable
String getVolumeUuid();
- /** @hide */
+ /**
+ * @see ApplicationInfo#zygotePreloadName
+ * @see R.styleable#AndroidManifestApplication_zygotePreloadName
+ * @hide
+ */
@Nullable
String getZygotePreloadName();
@@ -1073,36 +1122,42 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE
+ * @see R.styleable#AndroidManifestApplication_allowAudioPlaybackCapture
* @hide
*/
boolean isAllowAudioPlaybackCapture();
/**
* @see ApplicationInfo#FLAG_ALLOW_BACKUP
+ * @see R.styleable#AndroidManifestApplication_allowBackup
* @hide
*/
boolean isAllowBackup();
/**
* @see ApplicationInfo#FLAG_ALLOW_CLEAR_USER_DATA
+ * @see R.styleable#AndroidManifestApplication_allowClearUserData
* @hide
*/
boolean isAllowClearUserData();
/**
* @see ApplicationInfo#PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE
+ * @see R.styleable#AndroidManifestApplication_allowClearUserDataOnFailedRestore
* @hide
*/
boolean isAllowClearUserDataOnFailedRestore();
/**
* @see ApplicationInfo#PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING
+ * @see R.styleable#AndroidManifestApplication_allowNativeHeapPointerTagging
* @hide
*/
boolean isAllowNativeHeapPointerTagging();
/**
* @see ApplicationInfo#FLAG_ALLOW_TASK_REPARENTING
+ * @see R.styleable#AndroidManifestApplication_allowTaskReparenting
* @hide
*/
boolean isAllowTaskReparenting();
@@ -1122,18 +1177,21 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#PRIVATE_FLAG_BACKUP_IN_FOREGROUND
+ * @see R.styleable#AndroidManifestApplication_backupInForeground
* @hide
*/
boolean isBackupInForeground();
/**
* @see ApplicationInfo#FLAG_HARDWARE_ACCELERATED
+ * @see R.styleable#AndroidManifestApplication_hardwareAccelerated
* @hide
*/
boolean isBaseHardwareAccelerated();
/**
* @see ApplicationInfo#PRIVATE_FLAG_CANT_SAVE_STATE
+ * @see R.styleable#AndroidManifestApplication_cantSaveState
* @hide
*/
boolean isCantSaveState();
@@ -1146,18 +1204,21 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#crossProfile
+ * @see R.styleable#AndroidManifestApplication_crossProfile
* @hide
*/
boolean isCrossProfile();
/**
* @see ApplicationInfo#PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE
+ * @see R.styleable#AndroidManifestApplication_defaultToDeviceProtectedStorage
* @hide
*/
boolean isDefaultToDeviceProtectedStorage();
/**
* @see ApplicationInfo#PRIVATE_FLAG_DIRECT_BOOT_AWARE
+ * @see R.styleable#AndroidManifestApplication_directBootAware
* @hide
*/
boolean isDirectBootAware();
@@ -1177,6 +1238,7 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#FLAG_EXTRACT_NATIVE_LIBS
+ * @see R.styleable#AndroidManifestApplication_extractNativeLibs
* @hide
*/
boolean isExtractNativeLibs();
@@ -1195,12 +1257,14 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#FLAG_FULL_BACKUP_ONLY
+ * @see R.styleable#AndroidManifestApplication_fullBackupOnly
* @hide
*/
boolean isFullBackupOnly();
/**
* @see ApplicationInfo#FLAG_IS_GAME
+ * @see R.styleable#AndroidManifestApplication_isGame
* @hide
*/
@Deprecated
@@ -1208,30 +1272,35 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#FLAG_HAS_CODE
+ * @see R.styleable#AndroidManifestApplication_hasCode
* @hide
*/
boolean isHasCode();
/**
* @see ApplicationInfo#PRIVATE_FLAG_HAS_DOMAIN_URLS
+ * @see R.styleable#AndroidManifestIntentFilter
* @hide
*/
boolean isHasDomainUrls();
/**
* @see ApplicationInfo#PRIVATE_FLAG_HAS_FRAGILE_USER_DATA
+ * @see R.styleable#AndroidManifestApplication_hasFragileUserData
* @hide
*/
boolean isHasFragileUserData();
/**
* @see ApplicationInfo#FLAG_KILL_AFTER_RESTORE
+ * @see R.styleable#AndroidManifestApplication_killAfterRestore
* @hide
*/
boolean isKillAfterRestore();
/**
* @see ApplicationInfo#FLAG_LARGE_HEAP
+ * @see R.styleable#AndroidManifestApplication_largeHeap
* @hide
*/
boolean isLargeHeap();
@@ -1247,6 +1316,7 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#FLAG_MULTIARCH
+ * @see R.styleable#AndroidManifestApplication_multiArch
* @hide
*/
boolean isMultiArch();
@@ -1278,6 +1348,7 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#PRIVATE_FLAG_IS_RESOURCE_OVERLAY
* @see ApplicationInfo#isResourceOverlay()
+ * @see R.styleable#AndroidManifestResourceOverlay
* @hide
*/
boolean isOverlay();
@@ -1290,12 +1361,17 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE
+ * @see R.styleable#AndroidManifestActivity_directBootAware
+ * @see R.styleable#AndroidManifestProvider_directBootAware
+ * @see R.styleable#AndroidManifestReceiver_directBootAware
+ * @see R.styleable#AndroidManifestService_directBootAware
* @hide
*/
boolean isPartiallyDirectBootAware();
/**
* @see ApplicationInfo#FLAG_PERSISTENT
+ * @see R.styleable#AndroidManifestApplication_persistent
* @hide
*/
boolean isPersistent();
@@ -1314,18 +1390,21 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#PRIVATE_FLAG_EXT_PROFILEABLE
+ * @see R.styleable#AndroidManifestProfileable
* @hide
*/
boolean isProfileable();
/**
* @see ApplicationInfo#PRIVATE_FLAG_PROFILEABLE_BY_SHELL
+ * @see R.styleable#AndroidManifestProfileable_shell
* @hide
*/
boolean isProfileableByShell();
/**
* @see ApplicationInfo#PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE
+ * @see R.styleable#AndroidManifestApplication_requestLegacyExternalStorage
* @hide
*/
boolean isRequestLegacyExternalStorage();
@@ -1358,24 +1437,28 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION
+ * @see R.styleable#AppWidgetProviderInfo_resizeMode
* @hide
*/
boolean isResizeableActivityViaSdkVersion();
/**
* @see ApplicationInfo#FLAG_RESTORE_ANY_VERSION
+ * @see R.styleable#AndroidManifestApplication_restoreAnyVersion
* @hide
*/
boolean isRestoreAnyVersion();
/**
* True means that this package/app contains an SDK library.
+ * @see R.styleable#AndroidManifestSdkLibrary
* @hide
*/
boolean isSdkLibrary();
/**
* @see ApplicationInfo#PRIVATE_FLAG_STATIC_SHARED_LIBRARY
+ * @see R.styleable#AndroidManifestStaticLibrary
* @hide
*/
boolean isStaticSharedLibrary();
@@ -1417,6 +1500,7 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#FLAG_SUPPORTS_RTL
+ * @see R.styleable#AndroidManifestApplication_supportsRtl
* @hide
*/
boolean isSupportsRtl();
@@ -1445,6 +1529,7 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#FLAG_TEST_ONLY
+ * @see R.styleable#AndroidManifestApplication_testOnly
* @hide
*/
boolean isTestOnly();
@@ -1461,6 +1546,7 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#FLAG_USES_CLEARTEXT_TRAFFIC
+ * @see R.styleable#AndroidManifestApplication_usesCleartextTraffic
* @hide
*/
boolean isUsesCleartextTraffic();
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java b/services/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java
index a17ecc3d98aa..9024c27ebecb 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java
@@ -18,27 +18,58 @@ package com.android.server.pm.pkg;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.processor.immutability.Immutable;
+import com.android.internal.R;
+
import java.util.List;
-/** @hide */
+/**
+ * Representation of the parsed state of a single split APK. Note this includes the base.apk.
+ *
+ * The information here is very minimal, mostly used for loading a specific class, and most
+ * important state is collected across all splits for a package into the parent
+ * {@link AndroidPackage} values.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@Immutable
public interface AndroidPackageSplit {
+ /**
+ * @return The unique name given to the split, or null if this is the base.
+ */
@Nullable
String getName();
+ /**
+ * @return Physical location of the split APK on disk, pointing to a single file with the .apk
+ * extension.
+ */
@NonNull
String getPath();
+ /**
+ * @see R.styleable#AndroidManifest_revisionCode
+ */
int getRevisionCode();
+ /**
+ * @see R.styleable#AndroidManifestApplication_hasCode
+ */
boolean isHasCode();
+ /**
+ * @see R.styleable#AndroidManifestApplication_classLoader
+ */
@Nullable
String getClassLoaderName();
+ /**
+ * @see R.styleable#AndroidManifestUsesSplit
+ */
@NonNull
List<AndroidPackageSplit> getDependencies();
}
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java b/services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java
index 9aac8a851f05..c0f2c25a66a4 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java
@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
+/** @hide */
public class AndroidPackageSplitImpl implements AndroidPackageSplit {
@Nullable
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index fa1a63fc5986..3c79cdfa5c0e 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -16,29 +16,45 @@
package com.android.server.pm.pkg;
+import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.SystemApi;
import android.annotation.UserIdInt;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.SigningInfo;
import android.os.UserHandle;
import android.processor.immutability.Immutable;
import android.util.SparseArray;
+import com.android.internal.R;
+
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
- * The exposed system server process API for package data, shared with the internal
- * PackageManagerService implementation. All returned data is guaranteed immutable.
+ * A wrapper containing device-specific state for an application. It wraps the mostly stateless
+ * {@link AndroidPackage}, available through {@link #getAndroidPackage()}.
+ *
+ * Any fields whose values depend on dynamic state, disk location, enforcement policy,
+ * cross-package dependencies, system/device owner/admin configuration, etc. are placed in this
+ * interface.
+ *
+ * The backing memory is shared with the internal system server and thus there is no cost to
+ * access these objects, unless the public API equivalent {@link PackageInfo} or
+ * {@link ApplicationInfo}.
+ *
+ * This also means the data is immutable and will throw {@link UnsupportedOperationException} if
+ * any collection type is mutated.
*
* @hide
*/
-//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@Immutable
public interface PackageState {
@@ -92,9 +108,11 @@ public interface PackageState {
* The non-user-specific UID, or the UID if the user ID is
* {@link android.os.UserHandle#SYSTEM}.
*/
+ @AppIdInt
int getAppId();
/**
+ * @see PackageInfo#packageName
* @see AndroidPackage#getPackageName()
*/
@NonNull
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
index a68e59b2319a..366f8e513cfa 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
@@ -18,6 +18,7 @@ package com.android.server.pm.pkg;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.content.pm.PackageManager;
import android.content.pm.overlay.OverlayPaths;
import android.os.UserHandle;
@@ -28,12 +29,19 @@ import java.util.Map;
/**
- * The parent of this class is {@link PackageState}, which handles non-user state, exposing this
- * interface for per-user state.
+ * State for an app for a specific user, such as installed/enabled.
+ *
+ * Querying for a non-existent user does not throw an exception, so it is the responsibility of
+ * the caller to check for valid users if necessary. A default value assuming the app is installed
+ * for the non-existent user will be returned.
+ *
+ * The parent of this class is {@link PackageState}, which handles non-user state and holds one or
+ * many different {@link PackageUserState PackageUserStates}. This class is
+ * accessed through {@link PackageState#getStateForUser(UserHandle)}.
*
* @hide
*/
-//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@Immutable
public interface PackageUserState {
diff --git a/services/core/java/com/android/server/pm/pkg/SharedLibrary.java b/services/core/java/com/android/server/pm/pkg/SharedLibrary.java
index 20f05f684f6f..ee793f7ba653 100644
--- a/services/core/java/com/android/server/pm/pkg/SharedLibrary.java
+++ b/services/core/java/com/android/server/pm/pkg/SharedLibrary.java
@@ -18,6 +18,7 @@ package com.android.server.pm.pkg;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.VersionedPackage;
import android.processor.immutability.Immutable;
@@ -25,9 +26,22 @@ import android.processor.immutability.Immutable;
import java.util.List;
/**
+ * Information for a shared library dependency, which is resolved to a real package on the device.
+ *
+ * There are four types of shared libraries:
+ * <table>
+ * <tr><td>Built-in</td> <td>Non-updatable part of OS</td></tr>
+ * <tr><td>Dynamic</td> <td>Updatable backwards-compatible dynamically linked</td></tr>
+ * <tr><td>Static</td> <td>Non-backwards-compatible emulating static linking</td></tr>
+ * <tr><td>SDK</td> <td>Updatable backwards-incompatible dynamically loaded</td></tr>
+ * </table>
+ *
+ * This class is a clone of {@link SharedLibraryInfo} but as an interface with more guaranteed
+ * immutability.
+ *
* @hide
*/
-//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@Immutable
public interface SharedLibrary {
@@ -63,6 +77,7 @@ public interface SharedLibrary {
/**
* @see SharedLibraryInfo#getType()
*/
+ @SharedLibraryInfo.Type
int getType();
/**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4d5d607a1c89..ae998067fa03 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -63,7 +63,6 @@ import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER;
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
-import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
@@ -1593,7 +1592,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// If there's a dream running then use home to escape the dream
// but don't actually go home.
if (mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) {
- mDreamManagerInternal.stopDream(false /*immediate*/);
+ mDreamManagerInternal.stopDream(false /*immediate*/, "short press on home" /*reason*/);
return;
}
@@ -2859,9 +2858,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
case KeyEvent.KEYCODE_S:
if (down && event.isMetaPressed() && event.isCtrlPressed() && repeatCount == 0) {
- int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION
- : TAKE_SCREENSHOT_FULLSCREEN;
- interceptScreenshotChord(type, SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
+ interceptScreenshotChord(
+ TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
return key_consumed;
}
break;
@@ -4196,6 +4194,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mCameraGestureTriggered = true;
if (mRequestedOrSleepingDefaultDisplay) {
mCameraGestureTriggeredDuringGoingToSleep = true;
+ // Wake device up early to prevent display doing redundant turning off/on stuff.
+ wakeUpFromPowerKey(event.getDownTime());
}
return true;
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index f9352cb2b30f..ef787dcb2be5 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3278,8 +3278,10 @@ public final class PowerManagerService extends SystemService
if (mDreamManager != null) {
// Restart the dream whenever the sandman is summoned.
if (startDreaming) {
- mDreamManager.stopDream(/* immediate= */ false);
- mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);
+ mDreamManager.stopDream(/* immediate= */ false,
+ "power manager request before starting dream" /*reason*/);
+ mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING,
+ "power manager request" /*reason*/);
}
isDreaming = mDreamManager.isDreaming();
} else {
@@ -3364,7 +3366,7 @@ public final class PowerManagerService extends SystemService
// Stop dream.
if (isDreaming) {
- mDreamManager.stopDream(/* immediate= */ false);
+ mDreamManager.stopDream(/* immediate= */ false, "power manager request" /*reason*/);
}
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 8510bd369a2f..1e5b498e6c20 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -1331,6 +1331,13 @@ public class BatteryStatsImpl extends BatteryStats {
LongSamplingCounter mMobileRadioActiveUnknownTime;
LongSamplingCounter mMobileRadioActiveUnknownCount;
+ /**
+ * The soonest the Mobile Radio stats can be updated due to a mobile radio power state change
+ * after it was last updated.
+ */
+ @VisibleForTesting
+ protected static final long MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS = 1000 * 60 * 10;
+
int mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
@GuardedBy("this")
@@ -5531,6 +5538,15 @@ public class BatteryStatsImpl extends BatteryStats {
} else {
mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
+
+ if (mLastModemActivityInfo != null) {
+ if (elapsedRealtimeMs < mLastModemActivityInfo.getTimestampMillis()
+ + MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS) {
+ // Modem Activity info has been collected recently, don't bother
+ // triggering another update.
+ return false;
+ }
+ }
// Tell the caller to collect radio network/power stats.
return true;
}
diff --git a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
new file mode 100644
index 000000000000..5f76fbc0ec36
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2022 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.stats;
+
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+import android.util.LongSparseArray;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
+import android.util.TimeSparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IntPair;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Stores stats about CPU wakeups and tries to attribute them to subsystems and uids.
+ */
+public class CpuWakeupStats {
+ private static final String SUBSYSTEM_ALARM_STRING = "Alarm";
+ @VisibleForTesting
+ static final long WAKEUP_RETENTION_MS = 3 * 24 * 60 * 60_000; // 3 days.
+ @VisibleForTesting
+ static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
+
+ private final IrqDeviceMap mIrqDeviceMap;
+ private final WakingActivityHistory mRecentWakingActivity = new WakingActivityHistory();
+
+ @VisibleForTesting
+ final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>();
+ @VisibleForTesting
+ final TimeSparseArray<SparseArray<SparseBooleanArray>> mWakeupAttribution =
+ new TimeSparseArray<>();
+
+ public CpuWakeupStats(Context context, int mapRes) {
+ mIrqDeviceMap = IrqDeviceMap.getInstance(context, mapRes);
+ }
+
+ /** Notes a wakeup reason as reported by SuspendControlService to battery stats. */
+ public synchronized void noteWakeupTimeAndReason(long elapsedRealtime, long uptime,
+ String rawReason) {
+ final Wakeup parsedWakeup = new Wakeup(rawReason, elapsedRealtime, uptime);
+ mWakeupEvents.put(elapsedRealtime, parsedWakeup);
+ attemptAttributionFor(parsedWakeup);
+ // Assuming that wakeups always arrive in monotonically increasing elapsedRealtime order,
+ // we can delete all history that will not be useful in attributing future wakeups.
+ mRecentWakingActivity.clearAllBefore(elapsedRealtime - WAKEUP_REASON_HALF_WINDOW_MS);
+
+ // Limit history of wakeups and their attribution to the last WAKEUP_RETENTION_MS. Note that
+ // the last wakeup and its attribution (if computed) is always stored, even if that wakeup
+ // had occurred before WAKEUP_RETENTION_MS.
+ int lastIdx = mWakeupEvents.closestIndexOnOrBefore(elapsedRealtime - WAKEUP_RETENTION_MS);
+ for (int i = lastIdx; i >= 0; i--) {
+ mWakeupEvents.removeAt(i);
+ }
+ lastIdx = mWakeupAttribution.closestIndexOnOrBefore(elapsedRealtime - WAKEUP_RETENTION_MS);
+ for (int i = lastIdx; i >= 0; i--) {
+ mWakeupAttribution.removeAt(i);
+ }
+ }
+
+ /** Notes a waking activity that could have potentially woken up the CPU. */
+ public synchronized void noteWakingActivity(int subsystem, long elapsedRealtime, int... uids) {
+ if (!attemptAttributionWith(subsystem, elapsedRealtime, uids)) {
+ mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime, uids);
+ }
+ }
+
+ private synchronized void attemptAttributionFor(Wakeup wakeup) {
+ final SparseBooleanArray subsystems = getResponsibleSubsystemsForWakeup(wakeup);
+ if (subsystems == null) {
+ // We don't support attribution for this kind of wakeup yet.
+ return;
+ }
+
+ SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis);
+ if (attribution == null) {
+ attribution = new SparseArray<>();
+ mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
+ }
+
+ for (int subsystemIdx = 0; subsystemIdx < subsystems.size(); subsystemIdx++) {
+ final int subsystem = subsystems.keyAt(subsystemIdx);
+
+ // Blame all activity that happened WAKEUP_REASON_HALF_WINDOW_MS before or after
+ // the wakeup from each responsible subsystem.
+ final long startTime = wakeup.mElapsedMillis - WAKEUP_REASON_HALF_WINDOW_MS;
+ final long endTime = wakeup.mElapsedMillis + WAKEUP_REASON_HALF_WINDOW_MS;
+
+ final SparseBooleanArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem,
+ startTime, endTime);
+ attribution.put(subsystem, uidsToBlame);
+ }
+ }
+
+ private synchronized boolean attemptAttributionWith(int subsystem, long activityElapsed,
+ int... uids) {
+ final int startIdx = mWakeupEvents.closestIndexOnOrAfter(
+ activityElapsed - WAKEUP_REASON_HALF_WINDOW_MS);
+ final int endIdx = mWakeupEvents.closestIndexOnOrBefore(
+ activityElapsed + WAKEUP_REASON_HALF_WINDOW_MS);
+
+ for (int wakeupIdx = startIdx; wakeupIdx <= endIdx; wakeupIdx++) {
+ final Wakeup wakeup = mWakeupEvents.valueAt(wakeupIdx);
+ final SparseBooleanArray subsystems = getResponsibleSubsystemsForWakeup(wakeup);
+ if (subsystems == null) {
+ // Unsupported for attribution
+ continue;
+ }
+ if (subsystems.get(subsystem)) {
+ // We don't expect more than one wakeup to be found within such a short window, so
+ // just attribute this one and exit
+ SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(
+ wakeup.mElapsedMillis);
+ if (attribution == null) {
+ attribution = new SparseArray<>();
+ mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
+ }
+ SparseBooleanArray uidsToBlame = attribution.get(subsystem);
+ if (uidsToBlame == null) {
+ uidsToBlame = new SparseBooleanArray(uids.length);
+ attribution.put(subsystem, uidsToBlame);
+ }
+ for (final int uid : uids) {
+ uidsToBlame.put(uid, true);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Dumps the relevant stats for cpu wakeups and their attribution to subsystem and uids */
+ public synchronized void dump(IndentingPrintWriter pw, long nowElapsed) {
+ pw.println("CPU wakeup stats:");
+ pw.increaseIndent();
+
+ mIrqDeviceMap.dump(pw);
+ pw.println();
+
+ mRecentWakingActivity.dump(pw, nowElapsed);
+ pw.println();
+
+ final SparseLongArray attributionStats = new SparseLongArray();
+ pw.println("Wakeup events:");
+ pw.increaseIndent();
+ for (int i = mWakeupEvents.size() - 1; i >= 0; i--) {
+ TimeUtils.formatDuration(mWakeupEvents.keyAt(i), nowElapsed, pw);
+ pw.println(":");
+
+ pw.increaseIndent();
+ final Wakeup wakeup = mWakeupEvents.valueAt(i);
+ pw.println(wakeup);
+ pw.print("Attribution: ");
+ final SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(
+ wakeup.mElapsedMillis);
+ if (attribution == null) {
+ pw.println("N/A");
+ } else {
+ for (int subsystemIdx = 0; subsystemIdx < attribution.size(); subsystemIdx++) {
+ if (subsystemIdx > 0) {
+ pw.print(", ");
+ }
+ final long counters = attributionStats.get(attribution.keyAt(subsystemIdx),
+ IntPair.of(0, 0));
+ int attributed = IntPair.first(counters);
+ final int total = IntPair.second(counters) + 1;
+
+ pw.print("subsystem: " + subsystemToString(attribution.keyAt(subsystemIdx)));
+ pw.print(", uids: [");
+ final SparseBooleanArray uids = attribution.valueAt(subsystemIdx);
+ if (uids != null) {
+ for (int uidIdx = 0; uidIdx < uids.size(); uidIdx++) {
+ if (uidIdx > 0) {
+ pw.print(", ");
+ }
+ UserHandle.formatUid(pw, uids.keyAt(uidIdx));
+ }
+ attributed++;
+ }
+ pw.print("]");
+
+ attributionStats.put(attribution.keyAt(subsystemIdx),
+ IntPair.of(attributed, total));
+ }
+ pw.println();
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+
+ pw.println("Attribution stats:");
+ pw.increaseIndent();
+ for (int i = 0; i < attributionStats.size(); i++) {
+ pw.print("Subsystem " + subsystemToString(attributionStats.keyAt(i)));
+ pw.print(": ");
+ final long ratio = attributionStats.valueAt(i);
+ pw.println(IntPair.first(ratio) + "/" + IntPair.second(ratio));
+ }
+ pw.println("Total: " + mWakeupEvents.size());
+ pw.decreaseIndent();
+
+ pw.decreaseIndent();
+ pw.println();
+ }
+
+ private static final class WakingActivityHistory {
+ private static final long WAKING_ACTIVITY_RETENTION_MS = 3 * 60 * 60_000; // 3 hours.
+ private SparseArray<TimeSparseArray<SparseBooleanArray>> mWakingActivity =
+ new SparseArray<>();
+
+ void recordActivity(int subsystem, long elapsedRealtime, int... uids) {
+ if (uids == null) {
+ return;
+ }
+ TimeSparseArray<SparseBooleanArray> wakingActivity = mWakingActivity.get(subsystem);
+ if (wakingActivity == null) {
+ wakingActivity = new TimeSparseArray<>();
+ mWakingActivity.put(subsystem, wakingActivity);
+ }
+ SparseBooleanArray uidsToBlame = wakingActivity.get(elapsedRealtime);
+ if (uidsToBlame == null) {
+ uidsToBlame = new SparseBooleanArray(uids.length);
+ wakingActivity.put(elapsedRealtime, uidsToBlame);
+ }
+ for (int i = 0; i < uids.length; i++) {
+ uidsToBlame.put(uids[i], true);
+ }
+ // Limit activity history per subsystem to the last WAKING_ACTIVITY_RETENTION_MS.
+ // Note that the last activity is always present, even if it occurred before
+ // WAKING_ACTIVITY_RETENTION_MS.
+ final int endIdx = wakingActivity.closestIndexOnOrBefore(
+ elapsedRealtime - WAKING_ACTIVITY_RETENTION_MS);
+ for (int i = endIdx; i >= 0; i--) {
+ wakingActivity.removeAt(endIdx);
+ }
+ }
+
+ void clearAllBefore(long elapsedRealtime) {
+ for (int subsystemIdx = mWakingActivity.size() - 1; subsystemIdx >= 0; subsystemIdx--) {
+ final TimeSparseArray<SparseBooleanArray> activityPerSubsystem =
+ mWakingActivity.valueAt(subsystemIdx);
+ final int endIdx = activityPerSubsystem.closestIndexOnOrBefore(elapsedRealtime);
+ for (int removeIdx = endIdx; removeIdx >= 0; removeIdx--) {
+ activityPerSubsystem.removeAt(removeIdx);
+ }
+ // Generally waking activity is a high frequency occurrence for any subsystem, so we
+ // don't delete the TimeSparseArray even if it is now empty, to avoid object churn.
+ // This will leave one TimeSparseArray per subsystem, which are few right now.
+ }
+ }
+
+ SparseBooleanArray removeBetween(int subsystem, long startElapsed, long endElapsed) {
+ final SparseBooleanArray uidsToReturn = new SparseBooleanArray();
+
+ final TimeSparseArray<SparseBooleanArray> activityForSubsystem =
+ mWakingActivity.get(subsystem);
+ if (activityForSubsystem != null) {
+ final int startIdx = activityForSubsystem.closestIndexOnOrAfter(startElapsed);
+ final int endIdx = activityForSubsystem.closestIndexOnOrBefore(endElapsed);
+ for (int i = endIdx; i >= startIdx; i--) {
+ final SparseBooleanArray uidsForTime = activityForSubsystem.valueAt(i);
+ for (int j = 0; j < uidsForTime.size(); j++) {
+ if (uidsForTime.valueAt(j)) {
+ uidsToReturn.put(uidsForTime.keyAt(j), true);
+ }
+ }
+ }
+ // More efficient to remove in a separate loop as it avoids repeatedly calling gc().
+ for (int i = endIdx; i >= startIdx; i--) {
+ activityForSubsystem.removeAt(i);
+ }
+ // Generally waking activity is a high frequency occurrence for any subsystem, so we
+ // don't delete the TimeSparseArray even if it is now empty, to avoid object churn.
+ // This will leave one TimeSparseArray per subsystem, which are few right now.
+ }
+ return uidsToReturn.size() > 0 ? uidsToReturn : null;
+ }
+
+ void dump(IndentingPrintWriter pw, long nowElapsed) {
+ pw.println("Recent waking activity:");
+ pw.increaseIndent();
+ for (int i = 0; i < mWakingActivity.size(); i++) {
+ pw.println("Subsystem " + subsystemToString(mWakingActivity.keyAt(i)) + ":");
+ final LongSparseArray<SparseBooleanArray> wakingActivity =
+ mWakingActivity.valueAt(i);
+ if (wakingActivity == null) {
+ continue;
+ }
+ pw.increaseIndent();
+ for (int j = wakingActivity.size() - 1; j >= 0; j--) {
+ TimeUtils.formatDuration(wakingActivity.keyAt(j), nowElapsed, pw);
+ final SparseBooleanArray uidsToBlame = wakingActivity.valueAt(j);
+ if (uidsToBlame == null) {
+ pw.println();
+ continue;
+ }
+ pw.print(": ");
+ for (int k = 0; k < uidsToBlame.size(); k++) {
+ if (uidsToBlame.valueAt(k)) {
+ UserHandle.formatUid(pw, uidsToBlame.keyAt(k));
+ pw.print(", ");
+ }
+ }
+ pw.println();
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
+ }
+
+ private SparseBooleanArray getResponsibleSubsystemsForWakeup(Wakeup wakeup) {
+ if (ArrayUtils.isEmpty(wakeup.mDevices)) {
+ return null;
+ }
+ final SparseBooleanArray result = new SparseBooleanArray();
+ for (final Wakeup.IrqDevice device : wakeup.mDevices) {
+ final List<String> rawSubsystems = mIrqDeviceMap.getSubsystemsForDevice(device.mDevice);
+
+ boolean anyKnownSubsystem = false;
+ if (rawSubsystems != null) {
+ for (int i = 0; i < rawSubsystems.size(); i++) {
+ final int subsystem = stringToKnownSubsystem(rawSubsystems.get(i));
+ if (subsystem != CPU_WAKEUP_SUBSYSTEM_UNKNOWN) {
+ // Just in case the xml had arbitrary subsystem names, we want to make sure
+ // that we only put the known ones into our attribution map.
+ result.put(subsystem, true);
+ anyKnownSubsystem = true;
+ }
+ }
+ }
+ if (!anyKnownSubsystem) {
+ result.put(CPU_WAKEUP_SUBSYSTEM_UNKNOWN, true);
+ }
+ }
+ return result;
+ }
+
+ static int stringToKnownSubsystem(String rawSubsystem) {
+ switch (rawSubsystem) {
+ case SUBSYSTEM_ALARM_STRING:
+ return CPU_WAKEUP_SUBSYSTEM_ALARM;
+ }
+ return CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
+ }
+
+ static String subsystemToString(int subsystem) {
+ switch (subsystem) {
+ case CPU_WAKEUP_SUBSYSTEM_ALARM:
+ return SUBSYSTEM_ALARM_STRING;
+ case CPU_WAKEUP_SUBSYSTEM_UNKNOWN:
+ return "Unknown";
+ }
+ return "N/A";
+ }
+
+ private static final class Wakeup {
+ private static final String PARSER_TAG = "CpuWakeupStats.Wakeup";
+ private static final String ABORT_REASON_PREFIX = "Abort";
+ private static final Pattern sIrqPattern = Pattern.compile("(\\d+)\\s+(\\S+)");
+
+ String mRawReason;
+ long mElapsedMillis;
+ long mUptimeMillis;
+ IrqDevice[] mDevices;
+
+ Wakeup(String rawReason, long elapsedMillis, long uptimeMillis) {
+ mRawReason = rawReason;
+ mElapsedMillis = elapsedMillis;
+ mUptimeMillis = uptimeMillis;
+ mDevices = parseIrqDevices(rawReason);
+ }
+
+ private static IrqDevice[] parseIrqDevices(String rawReason) {
+ final String[] components = rawReason.split(":");
+ if (ArrayUtils.isEmpty(components) || components[0].startsWith(ABORT_REASON_PREFIX)) {
+ // We don't support parsing aborts yet.
+ return null;
+ }
+
+ int parsedDeviceCount = 0;
+ IrqDevice[] parsedDevices = new IrqDevice[components.length];
+
+ for (String component : components) {
+ final Matcher matcher = sIrqPattern.matcher(component);
+ if (matcher.find()) {
+ final int line;
+ final String device;
+ try {
+ line = Integer.parseInt(matcher.group(1));
+ device = matcher.group(2);
+ } catch (NumberFormatException e) {
+ Slog.e(PARSER_TAG,
+ "Exception while parsing device names from part: " + component, e);
+ continue;
+ }
+ parsedDevices[parsedDeviceCount++] = new IrqDevice(line, device);
+ }
+ }
+ return (parsedDeviceCount > 0) ? Arrays.copyOf(parsedDevices, parsedDeviceCount) : null;
+ }
+
+ @Override
+ public String toString() {
+ return "Wakeup{"
+ + "mRawReason='" + mRawReason + '\''
+ + ", mElapsedMillis=" + mElapsedMillis
+ + ", mUptimeMillis=" + TimeUtils.formatDuration(mUptimeMillis)
+ + ", mDevices=" + Arrays.toString(mDevices)
+ + '}';
+ }
+
+ static final class IrqDevice {
+ int mLine;
+ String mDevice;
+
+ IrqDevice(int line, String device) {
+ mLine = line;
+ mDevice = device;
+ }
+
+ @Override
+ public String toString() {
+ return "IrqDevice{" + "mLine=" + mLine + ", mDevice='" + mDevice + '\'' + '}';
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/IrqDeviceMap.java b/services/core/java/com/android/server/power/stats/IrqDeviceMap.java
new file mode 100644
index 000000000000..091d18e30ccc
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/IrqDeviceMap.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 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.stats;
+
+import android.annotation.XmlRes;
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.LongSparseArray;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Parses irq_device_map.xml to store a mapping of devices that can send IRQs to the CPU to
+ * subsystems that represent some logical work happening on the device that could need an IRQ.
+ */
+public class IrqDeviceMap {
+ private static final String TAG_IRQ_DEVICE_MAP = "irq-device-map";
+ private static final String TAG_DEVICE = "device";
+ private static final String TAG_SUBSYSTEM = "subsystem";
+ private static final String ATTR_NAME = "name";
+
+ private static LongSparseArray<IrqDeviceMap> sInstanceMap = new LongSparseArray<>(1);
+
+ private final ArrayMap<String, List<String>> mSubsystemsForDevice = new ArrayMap();
+
+ private IrqDeviceMap(XmlResourceParser parser) {
+ try {
+ XmlUtils.beginDocument(parser, TAG_IRQ_DEVICE_MAP);
+
+ int type;
+ String currentDevice = null;
+ final ArraySet<String> subsystems = new ArraySet<>();
+
+ while ((type = parser.getEventType()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_DEVICE)) {
+ currentDevice = parser.getAttributeValue(null, ATTR_NAME);
+ }
+ if (currentDevice != null && type == XmlPullParser.END_TAG
+ && parser.getName().equals(TAG_DEVICE)) {
+ final int n = subsystems.size();
+ if (n > 0) {
+ mSubsystemsForDevice.put(currentDevice,
+ Collections.unmodifiableList(new ArrayList<>(subsystems)));
+ }
+ subsystems.clear();
+ currentDevice = null;
+ }
+ if (currentDevice != null && type == XmlPullParser.START_TAG
+ && parser.getName().equals(TAG_SUBSYSTEM)) {
+ parser.next();
+ if (parser.getEventType() == XmlPullParser.TEXT) {
+ subsystems.add(parser.getText());
+ }
+ }
+ parser.next();
+ }
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ parser.close();
+ }
+ }
+
+ /**
+ * Returns an instance of IrqDeviceMap initialzed with the given context and xml resource.
+ * The xml resource should describe the mapping in a way similar to
+ * core/res/res/xml/irq_device_map.xml.
+ */
+ public static IrqDeviceMap getInstance(Context context, @XmlRes int resId) {
+ synchronized (IrqDeviceMap.class) {
+ final int idx = sInstanceMap.indexOfKey(resId);
+ if (idx >= 0) {
+ return sInstanceMap.valueAt(idx);
+ }
+ }
+ final XmlResourceParser parser = context.getResources().getXml(resId);
+ final IrqDeviceMap irqDeviceMap = new IrqDeviceMap(parser);
+ synchronized (IrqDeviceMap.class) {
+ sInstanceMap.put(resId, irqDeviceMap);
+ }
+ return irqDeviceMap;
+ }
+
+ List<String> getSubsystemsForDevice(String device) {
+ return mSubsystemsForDevice.get(device);
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ pw.println("Irq device map:");
+ pw.increaseIndent();
+
+ final LongSparseArray<IrqDeviceMap> instanceMap;
+ synchronized (IrqDeviceMap.class) {
+ instanceMap = sInstanceMap;
+ }
+ final int idx = instanceMap.indexOfValue(this);
+ final String res = (idx >= 0) ? ("0x" + Long.toHexString(instanceMap.keyAt(idx))) : null;
+ pw.println("Loaded from xml resource: " + res);
+
+ pw.println("Map:");
+ pw.increaseIndent();
+ for (int i = 0; i < mSubsystemsForDevice.size(); i++) {
+ pw.print(mSubsystemsForDevice.keyAt(i) + ": ");
+ pw.println(mSubsystemsForDevice.valueAt(i));
+ }
+ pw.decreaseIndent();
+
+ pw.decreaseIndent();
+ }
+}
diff --git a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java
index d9a4266e2812..4f221b532b75 100644
--- a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java
@@ -155,21 +155,33 @@ public final class ConfigurationInternal {
return UserHandle.of(mUserId);
}
- /** Returns true if the user allowed to modify time zone configuration. */
+ /**
+ * Returns true if the user is allowed to modify time configuration, e.g. can be false due
+ * to device policy (enterprise).
+ *
+ * <p>See also {@link #createCapabilitiesAndConfig(boolean)} for situations where this
+ * value are ignored.
+ */
public boolean isUserConfigAllowed() {
return mUserConfigAllowed;
}
- /** Returns a {@link TimeCapabilitiesAndConfig} objects based on configuration values. */
- public TimeCapabilitiesAndConfig capabilitiesAndConfig() {
- return new TimeCapabilitiesAndConfig(timeCapabilities(), timeConfiguration());
+ /**
+ * Returns a {@link TimeCapabilitiesAndConfig} objects based on configuration values.
+ *
+ * @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device
+ * policy restrictions that should apply to actual users can be ignored
+ */
+ public TimeCapabilitiesAndConfig createCapabilitiesAndConfig(boolean bypassUserPolicyChecks) {
+ return new TimeCapabilitiesAndConfig(
+ timeCapabilities(bypassUserPolicyChecks), timeConfiguration());
}
- private TimeCapabilities timeCapabilities() {
+ private TimeCapabilities timeCapabilities(boolean bypassUserPolicyChecks) {
UserHandle userHandle = UserHandle.of(mUserId);
TimeCapabilities.Builder builder = new TimeCapabilities.Builder(userHandle);
- boolean allowConfigDateTime = isUserConfigAllowed();
+ boolean allowConfigDateTime = isUserConfigAllowed() || bypassUserPolicyChecks;
boolean deviceHasAutoTimeDetection = isAutoDetectionSupported();
final @CapabilityState int configureAutoDetectionEnabledCapability;
@@ -186,15 +198,15 @@ public final class ConfigurationInternal {
// current logic above, this could lead to a situation where a device hardware does not
// support auto detection, the device has been forced into "auto" mode by an admin and the
// user is unable to disable auto detection.
- final @CapabilityState int suggestManualTimeZoneCapability;
+ final @CapabilityState int suggestManualTimeCapability;
if (!allowConfigDateTime) {
- suggestManualTimeZoneCapability = CAPABILITY_NOT_ALLOWED;
+ suggestManualTimeCapability = CAPABILITY_NOT_ALLOWED;
} else if (getAutoDetectionEnabledBehavior()) {
- suggestManualTimeZoneCapability = CAPABILITY_NOT_APPLICABLE;
+ suggestManualTimeCapability = CAPABILITY_NOT_APPLICABLE;
} else {
- suggestManualTimeZoneCapability = CAPABILITY_POSSESSED;
+ suggestManualTimeCapability = CAPABILITY_POSSESSED;
}
- builder.setSetManualTimeCapability(suggestManualTimeZoneCapability);
+ builder.setSetManualTimeCapability(suggestManualTimeCapability);
return builder.build();
}
diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java
index 25a74ceeb56d..a39f64c1f82a 100644
--- a/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java
@@ -53,11 +53,15 @@ public interface ServiceConfigAccessor {
/**
* Updates the configuration properties that control a device's time behavior.
*
- * <p>This method returns {@code true} if the configuration was changed,
- * {@code false} otherwise.
+ * <p>This method returns {@code true} if the configuration was changed, {@code false}
+ * otherwise.
+ *
+ * @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device
+ * policy restrictions that should apply to actual users can be ignored
*/
boolean updateConfiguration(
- @UserIdInt int userId, @NonNull TimeConfiguration requestedConfiguration);
+ @UserIdInt int userId, @NonNull TimeConfiguration requestedConfiguration,
+ boolean bypassUserPolicyChecks);
/**
* Returns a snapshot of the configuration that controls time zone detector behavior for the
diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java
index 84013a755035..71acf35bd3a2 100644
--- a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java
+++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java
@@ -194,11 +194,11 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
@Override
public synchronized boolean updateConfiguration(@UserIdInt int userId,
- @NonNull TimeConfiguration requestedConfiguration) {
+ @NonNull TimeConfiguration requestedConfiguration, boolean bypassUserPolicyChecks) {
Objects.requireNonNull(requestedConfiguration);
- TimeCapabilitiesAndConfig capabilitiesAndConfig =
- getCurrentUserConfigurationInternal().capabilitiesAndConfig();
+ TimeCapabilitiesAndConfig capabilitiesAndConfig = getCurrentUserConfigurationInternal()
+ .createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
TimeConfiguration oldConfiguration = capabilitiesAndConfig.getConfiguration();
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java b/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java
index eae12c28d2b9..24533d79fb18 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java
@@ -17,16 +17,47 @@
package com.android.server.timedetector;
import android.annotation.NonNull;
+import android.app.time.TimeCapabilitiesAndConfig;
+import android.app.time.TimeConfiguration;
+import android.app.timedetector.ManualTimeSuggestion;
/**
- * The internal (in-process) system server API for the {@link
- * com.android.server.timedetector.TimeDetectorService}.
+ * The internal (in-process) system server API for the time detector service.
*
* <p>The methods on this class can be called from any thread.
+ *
+ * <p>Methods marked with "[For device policy manager only]" are for use by the device policy
+ * manager to set device state and must not enforce device policy restrictions.
+ *
* @hide
*/
public interface TimeDetectorInternal {
+ /**
+ * [For device policy manager only] Returns a snapshot of the configuration that controls time
+ * detector behavior for the current user.
+ */
+ @NonNull
+ TimeCapabilitiesAndConfig getCapabilitiesAndConfigForDpm();
+
+ /**
+ * [For device policy manager only] Updates the configuration properties that control a device's
+ * time behavior for the current user.
+ *
+ * <p>This method returns {@code true} if the configuration was changed, {@code false}
+ * otherwise.
+ */
+ boolean updateConfigurationForDpm(@NonNull TimeConfiguration configuration);
+
+ /**
+ * [For device policy manager only] Attempts to set the device to a manually entered time.
+ * Returns {@code false} if the suggestion is invalid, or the device configuration prevents the
+ * suggestion being used, {@code true} if the suggestion has been accepted. A suggestion that is
+ * valid but does not change the time because it matches the current device time is considered
+ * accepted.
+ */
+ boolean setManualTimeForDpm(@NonNull ManualTimeSuggestion manualTimeSuggestion);
+
/** Used to pass new network time suggestions to the time detector. */
void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal);
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java
index 5a3e20ebd6cd..9839de080690 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java
@@ -17,9 +17,14 @@
package com.android.server.timedetector;
import android.annotation.NonNull;
+import android.app.time.TimeCapabilitiesAndConfig;
+import android.app.time.TimeConfiguration;
+import android.app.timedetector.ManualTimeSuggestion;
import android.content.Context;
import android.os.Handler;
+import com.android.server.timezonedetector.CurrentUserIdentityInjector;
+
import java.util.Objects;
/**
@@ -31,16 +36,50 @@ public class TimeDetectorInternalImpl implements TimeDetectorInternal {
@NonNull private final Context mContext;
@NonNull private final Handler mHandler;
+ @NonNull private final CurrentUserIdentityInjector mCurrentUserIdentityInjector;
+ @NonNull private final ServiceConfigAccessor mServiceConfigAccessor;
@NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
public TimeDetectorInternalImpl(@NonNull Context context, @NonNull Handler handler,
+ @NonNull CurrentUserIdentityInjector currentUserIdentityInjector,
+ @NonNull ServiceConfigAccessor serviceConfigAccessor,
@NonNull TimeDetectorStrategy timeDetectorStrategy) {
mContext = Objects.requireNonNull(context);
mHandler = Objects.requireNonNull(handler);
+ mCurrentUserIdentityInjector = Objects.requireNonNull(currentUserIdentityInjector);
+ mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy);
}
@Override
+ @NonNull
+ public TimeCapabilitiesAndConfig getCapabilitiesAndConfigForDpm() {
+ int currentUserId = mCurrentUserIdentityInjector.getCurrentUserId();
+ final boolean bypassUserPolicyCheck = true;
+ ConfigurationInternal configurationInternal =
+ mServiceConfigAccessor.getConfigurationInternal(currentUserId);
+ return configurationInternal.createCapabilitiesAndConfig(bypassUserPolicyCheck);
+ }
+
+ @Override
+ public boolean updateConfigurationForDpm(@NonNull TimeConfiguration configuration) {
+ Objects.requireNonNull(configuration);
+
+ int currentUserId = mCurrentUserIdentityInjector.getCurrentUserId();
+ final boolean bypassUserPolicyCheck = true;
+ return mServiceConfigAccessor.updateConfiguration(
+ currentUserId, configuration, bypassUserPolicyCheck);
+ }
+
+ @Override
+ public boolean setManualTimeForDpm(@NonNull ManualTimeSuggestion timeSignal) {
+ Objects.requireNonNull(timeSignal);
+
+ int userId = mCurrentUserIdentityInjector.getCurrentUserId();
+ return mTimeDetectorStrategy.suggestManualTime(userId, timeSignal, false);
+ }
+
+ @Override
public void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) {
Objects.requireNonNull(timeSignal);
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 39672b847f30..64adbb680653 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -49,6 +49,7 @@ import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.SystemService;
import com.android.server.timezonedetector.CallerIdentityInjector;
+import com.android.server.timezonedetector.CurrentUserIdentityInjector;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -84,8 +85,11 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
TimeDetectorStrategyImpl.create(context, handler, serviceConfigAccessor);
// Create and publish the local service for use by internal callers.
- TimeDetectorInternal internal =
- new TimeDetectorInternalImpl(context, handler, timeDetectorStrategy);
+ CurrentUserIdentityInjector currentUserIdentityInjector =
+ CurrentUserIdentityInjector.REAL;
+ TimeDetectorInternal internal = new TimeDetectorInternalImpl(
+ context, handler, currentUserIdentityInjector, serviceConfigAccessor,
+ timeDetectorStrategy);
publishLocalService(TimeDetectorInternal.class, internal);
CallerIdentityInjector callerIdentityInjector = CallerIdentityInjector.REAL;
@@ -147,7 +151,8 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
try {
ConfigurationInternal configurationInternal =
mServiceConfigAccessor.getConfigurationInternal(userId);
- return configurationInternal.capabilitiesAndConfig();
+ final boolean bypassUserPolicyCheck = false;
+ return configurationInternal.createCapabilitiesAndConfig(bypassUserPolicyCheck);
} finally {
mCallerIdentityInjector.restoreCallingIdentity(token);
}
@@ -170,7 +175,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
- return mServiceConfigAccessor.updateConfiguration(resolvedUserId, configuration);
+ final boolean bypassUserPolicyCheck = false;
+ return mServiceConfigAccessor.updateConfiguration(
+ resolvedUserId, configuration, bypassUserPolicyCheck);
} finally {
mCallerIdentityInjector.restoreCallingIdentity(token);
}
@@ -313,7 +320,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
int userId = mCallerIdentityInjector.getCallingUserId();
final long token = Binder.clearCallingIdentity();
try {
- return mTimeDetectorStrategy.suggestManualTime(userId, timeSignal);
+ final boolean bypassUserPolicyChecks = false;
+ return mTimeDetectorStrategy.suggestManualTime(
+ userId, timeSignal, bypassUserPolicyChecks);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -335,7 +344,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
int userId = mCallerIdentityInjector.getCallingUserId();
final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
- return mTimeDetectorStrategy.suggestManualTime(userId, timeSignal);
+ final boolean bypassUserPolicyChecks = false;
+ return mTimeDetectorStrategy.suggestManualTime(
+ userId, timeSignal, bypassUserPolicyChecks);
} finally {
mCallerIdentityInjector.restoreCallingIdentity(token);
}
@@ -400,19 +411,19 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
}
private void enforceSuggestManualTimePermission() {
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE,
"suggest manual time and time zone");
}
private void enforceSuggestNetworkTimePermission() {
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
android.Manifest.permission.SET_TIME,
"set time");
}
private void enforceSuggestGnssTimePermission() {
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
android.Manifest.permission.SET_TIME,
"suggest gnss time");
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index bc86ed057fb6..03f236d9b30d 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -93,8 +93,12 @@ public interface TimeDetectorStrategy extends Dumpable {
* invalid, or the device configuration prevented the suggestion being used, {@code true} if the
* suggestion was accepted. A suggestion that is valid but does not change the time because it
* matches the current device time is considered accepted.
+ *
+ * @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device
+ * policy restrictions that should apply to actual users can be ignored
*/
- boolean suggestManualTime(@UserIdInt int userId, @NonNull ManualTimeSuggestion timeSuggestion);
+ boolean suggestManualTime(@UserIdInt int userId, @NonNull ManualTimeSuggestion timeSuggestion,
+ boolean bypassUserPolicyChecks);
/** Processes the suggested time from network sources. */
void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion);
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index c3f05cc22495..3cee19cbe385 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -16,6 +16,8 @@
package com.android.server.timedetector;
+import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
+
import static com.android.server.SystemClockTime.TIME_CONFIDENCE_HIGH;
import static com.android.server.SystemClockTime.TIME_CONFIDENCE_LOW;
import static com.android.server.timedetector.TimeDetectorStrategy.originToString;
@@ -26,6 +28,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.time.ExternalTimeSuggestion;
+import android.app.time.TimeCapabilities;
+import android.app.time.TimeCapabilitiesAndConfig;
import android.app.time.TimeState;
import android.app.time.UnixEpochTime;
import android.app.timedetector.ManualTimeSuggestion;
@@ -244,7 +248,8 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
@Override
public synchronized boolean suggestManualTime(
- @UserIdInt int userId, @NonNull ManualTimeSuggestion suggestion) {
+ @UserIdInt int userId, @NonNull ManualTimeSuggestion suggestion,
+ boolean bypassUserPolicyChecks) {
ConfigurationInternal currentUserConfig = mCurrentConfigurationInternal;
if (currentUserConfig.getUserId() != userId) {
@@ -256,6 +261,18 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
}
Objects.requireNonNull(suggestion);
+ String cause = "Manual time suggestion received: suggestion=" + suggestion;
+
+ TimeCapabilitiesAndConfig capabilitiesAndConfig =
+ currentUserConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
+ TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
+ if (capabilities.getSetManualTimeCapability() != CAPABILITY_POSSESSED) {
+ Slog.i(LOG_TAG, "User does not have the capability needed to set the time manually"
+ + ": capabilities=" + capabilities
+ + ", suggestion=" + suggestion
+ + ", cause=" + cause);
+ return false;
+ }
final UnixEpochTime newUnixEpochTime = suggestion.getUnixEpochTime();
@@ -263,7 +280,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
return false;
}
- String cause = "Manual time suggestion received: suggestion=" + suggestion;
return setSystemClockAndConfidenceIfRequired(ORIGIN_MANUAL, newUnixEpochTime, cause);
}
@@ -428,7 +444,10 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
ipw.println("mCurrentConfigurationInternal=" + mCurrentConfigurationInternal);
- ipw.println("[Capabilities=" + mCurrentConfigurationInternal.capabilitiesAndConfig() + "]");
+ final boolean bypassUserPolicyChecks = false;
+ ipw.println("[Capabilities="
+ + mCurrentConfigurationInternal.createCapabilitiesAndConfig(bypassUserPolicyChecks)
+ + "]");
long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
ipw.printf("mEnvironment.elapsedRealtimeMillis()=%s (%s)\n",
Duration.ofMillis(elapsedRealtimeMillis), elapsedRealtimeMillis);
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index d413febdcdb4..8e2a5f472558 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -147,7 +147,13 @@ public final class ConfigurationInternal {
return UserHandle.of(mUserId);
}
- /** Returns true if the user allowed to modify time zone configuration. */
+ /**
+ * Returns true if the user is allowed to modify time zone configuration, e.g. can be false due
+ * to device policy (enterprise).
+ *
+ * <p>See also {@link #createCapabilitiesAndConfig(boolean)} for situations where this value
+ * are ignored.
+ */
public boolean isUserConfigAllowed() {
return mUserConfigAllowed;
}
@@ -190,17 +196,24 @@ public final class ConfigurationInternal {
|| getGeoDetectionRunInBackgroundEnabled());
}
- /** Creates a {@link TimeZoneCapabilitiesAndConfig} object using the configuration values. */
- public TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig() {
- return new TimeZoneCapabilitiesAndConfig(asCapabilities(), asConfiguration());
+ /**
+ * Creates a {@link TimeZoneCapabilitiesAndConfig} object using the configuration values.
+ *
+ * @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device
+ * policy restrictions that should apply to actual users can be ignored
+ */
+ public TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig(
+ boolean bypassUserPolicyChecks) {
+ return new TimeZoneCapabilitiesAndConfig(
+ asCapabilities(bypassUserPolicyChecks), asConfiguration());
}
@NonNull
- private TimeZoneCapabilities asCapabilities() {
+ private TimeZoneCapabilities asCapabilities(boolean bypassUserPolicyChecks) {
UserHandle userHandle = UserHandle.of(mUserId);
TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(userHandle);
- boolean allowConfigDateTime = isUserConfigAllowed();
+ boolean allowConfigDateTime = isUserConfigAllowed() || bypassUserPolicyChecks;
// Automatic time zone detection is only supported on devices if there is a telephony
// network available or geolocation time zone detection is possible.
diff --git a/services/core/java/com/android/server/timezonedetector/CurrentUserIdentityInjector.java b/services/core/java/com/android/server/timezonedetector/CurrentUserIdentityInjector.java
new file mode 100644
index 000000000000..f96b76dca929
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/CurrentUserIdentityInjector.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022 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.timezonedetector;
+
+import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+
+import com.android.server.LocalServices;
+
+/**
+ * An interface to access the current user identity in an easy to fake for tests way.
+ */
+public interface CurrentUserIdentityInjector {
+
+ /** A singleton for the real implementation of {@link CurrentUserIdentityInjector}. */
+ CurrentUserIdentityInjector REAL = new Real();
+
+ /** A {@link ActivityManagerInternal#getCurrentUserId()} call. */
+ @UserIdInt int getCurrentUserId();
+
+ /** The real implementation of {@link CurrentUserIdentityInjector}. */
+ class Real implements CurrentUserIdentityInjector {
+
+ protected Real() {
+ }
+
+ @Override
+ public int getCurrentUserId() {
+ return LocalServices.getService(ActivityManagerInternal.class).getCurrentUserId();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 692b0cc795f5..8da5d6aefdd6 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -81,11 +81,15 @@ public interface ServiceConfigAccessor {
/**
* Updates the configuration properties that control a device's time zone behavior.
*
- * <p>This method returns {@code true} if the configuration was changed,
- * {@code false} otherwise.
+ * <p>This method returns {@code true} if the configuration was changed, {@code false}
+ * otherwise.
+ *
+ * @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device
+ * policy restrictions that should apply to actual users can be ignored
*/
- boolean updateConfiguration(@UserIdInt int userId,
- @NonNull TimeZoneConfiguration requestedConfiguration);
+ boolean updateConfiguration(
+ @UserIdInt int userId, @NonNull TimeZoneConfiguration requestedConfiguration,
+ boolean bypassUserPolicyChecks);
/**
* Returns a snapshot of the configuration that controls time zone detector behavior for the
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
index 7173f6008177..e2f42467c550 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
@@ -234,11 +234,11 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
@Override
public synchronized boolean updateConfiguration(@UserIdInt int userId,
- @NonNull TimeZoneConfiguration requestedConfiguration) {
+ @NonNull TimeZoneConfiguration requestedConfiguration, boolean bypassUserPolicyChecks) {
Objects.requireNonNull(requestedConfiguration);
- TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
- getConfigurationInternal(userId).createCapabilitiesAndConfig();
+ TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = getConfigurationInternal(userId)
+ .createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
TimeZoneConfiguration oldConfiguration = capabilitiesAndConfig.getConfiguration();
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index b6ce8026a2dc..80cf1d6b9031 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -17,17 +17,48 @@
package com.android.server.timezonedetector;
import android.annotation.NonNull;
+import android.app.time.TimeZoneCapabilitiesAndConfig;
+import android.app.time.TimeZoneConfiguration;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
/**
- * The internal (in-process) system server API for the {@link
- * com.android.server.timezonedetector.TimeZoneDetectorService}.
+ * The internal (in-process) system server API for the time zone detector service.
*
* <p>The methods on this class can be called from any thread.
+ *
+ * <p>Methods marked with "[For device policy manager only]" are for use by the device policy
+ * manager to set device state and must not enforce device policy restrictions.
+ *
* @hide
*/
public interface TimeZoneDetectorInternal {
/**
+ * [For device policy manager only] Returns a snapshot of the configuration that controls time
+ * zone detector behavior for the current user.
+ */
+ @NonNull
+ TimeZoneCapabilitiesAndConfig getCapabilitiesAndConfigForDpm();
+
+ /**
+ * [For device policy manager only] Updates the configuration properties that control a device's
+ * time zone behavior for the current user.
+ *
+ * <p>This method returns {@code true} if the configuration was changed,
+ * {@code false} otherwise.
+ */
+ boolean updateConfigurationForDpm(@NonNull TimeZoneConfiguration configuration);
+
+ /**
+ * [For device policy manager only] Attempts to set the device to a manually entered time zone.
+ * Returns {@code false} if the suggestion is invalid, or the device configuration prevents the
+ * suggestion being used, {@code true} if the suggestion has been accepted. A suggestion that is
+ * valid but does not change the time zone because it matches the current device time zone is
+ * considered accepted.
+ */
+ boolean setManualTimeZoneForDpm(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion);
+
+ /**
* Suggests the current time zone, determined using geolocation, to the detector. The
* detector may ignore the signal based on system settings, whether better information is
* available, and so on. This method may be implemented asynchronously.
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index f61df820c3e0..ce64eaccd98f 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -17,6 +17,9 @@
package com.android.server.timezonedetector;
import android.annotation.NonNull;
+import android.app.time.TimeZoneCapabilitiesAndConfig;
+import android.app.time.TimeZoneConfiguration;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.content.Context;
import android.os.Handler;
@@ -31,16 +34,52 @@ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInter
@NonNull private final Context mContext;
@NonNull private final Handler mHandler;
+ @NonNull private final CurrentUserIdentityInjector mCurrentUserIdentityInjector;
+ @NonNull private final ServiceConfigAccessor mServiceConfigAccessor;
@NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
public TimeZoneDetectorInternalImpl(@NonNull Context context, @NonNull Handler handler,
+ @NonNull CurrentUserIdentityInjector currentUserIdentityInjector,
+ @NonNull ServiceConfigAccessor serviceConfigAccessor,
@NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
mContext = Objects.requireNonNull(context);
mHandler = Objects.requireNonNull(handler);
+ mCurrentUserIdentityInjector = Objects.requireNonNull(currentUserIdentityInjector);
+ mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy);
}
@Override
+ @NonNull
+ public TimeZoneCapabilitiesAndConfig getCapabilitiesAndConfigForDpm() {
+ int currentUserId = mCurrentUserIdentityInjector.getCurrentUserId();
+ ConfigurationInternal configurationInternal =
+ mServiceConfigAccessor.getConfigurationInternal(currentUserId);
+ final boolean bypassUserPolicyChecks = true;
+ return configurationInternal.createCapabilitiesAndConfig(bypassUserPolicyChecks);
+ }
+
+ @Override
+ public boolean updateConfigurationForDpm(@NonNull TimeZoneConfiguration configuration) {
+ Objects.requireNonNull(configuration);
+
+ int currentUserId = mCurrentUserIdentityInjector.getCurrentUserId();
+ final boolean bypassUserPolicyChecks = true;
+ return mServiceConfigAccessor.updateConfiguration(
+ currentUserId, configuration, bypassUserPolicyChecks);
+ }
+
+ @Override
+ public boolean setManualTimeZoneForDpm(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
+ Objects.requireNonNull(timeZoneSuggestion);
+
+ int currentUserId = mCurrentUserIdentityInjector.getCurrentUserId();
+ final boolean bypassUserPolicyChecks = true;
+ return mTimeZoneDetectorStrategy.suggestManualTimeZone(
+ currentUserId, timeZoneSuggestion, bypassUserPolicyChecks);
+ }
+
+ @Override
public void suggestGeolocationTimeZone(
@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
Objects.requireNonNull(timeZoneSuggestion);
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 822cd4124380..13f169461511 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -96,8 +96,11 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
});
// Create and publish the local service for use by internal callers.
- TimeZoneDetectorInternal internal =
- new TimeZoneDetectorInternalImpl(context, handler, timeZoneDetectorStrategy);
+ CurrentUserIdentityInjector currentUserIdentityInjector =
+ CurrentUserIdentityInjector.REAL;
+ TimeZoneDetectorInternal internal = new TimeZoneDetectorInternalImpl(
+ context, handler, currentUserIdentityInjector, serviceConfigAccessor,
+ timeZoneDetectorStrategy);
publishLocalService(TimeZoneDetectorInternal.class, internal);
// Publish the binder service so it can be accessed from other (appropriately
@@ -175,7 +178,8 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
try {
ConfigurationInternal configurationInternal =
mServiceConfigAccessor.getConfigurationInternal(userId);
- return configurationInternal.createCapabilitiesAndConfig();
+ final boolean bypassUserPolicyChecks = false;
+ return configurationInternal.createCapabilitiesAndConfig(bypassUserPolicyChecks);
} finally {
mCallerIdentityInjector.restoreCallingIdentity(token);
}
@@ -199,7 +203,9 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
- return mServiceConfigAccessor.updateConfiguration(resolvedUserId, configuration);
+ final boolean bypassUserPolicyChecks = false;
+ return mServiceConfigAccessor.updateConfiguration(
+ resolvedUserId, configuration, bypassUserPolicyChecks);
} finally {
mCallerIdentityInjector.restoreCallingIdentity(token);
}
@@ -350,7 +356,9 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
int userId = mCallerIdentityInjector.getCallingUserId();
final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
- return mTimeZoneDetectorStrategy.suggestManualTimeZone(userId, timeZoneSuggestion);
+ final boolean bypassUserPolicyChecks = false;
+ return mTimeZoneDetectorStrategy.suggestManualTimeZone(
+ userId, timeZoneSuggestion, bypassUserPolicyChecks);
} finally {
mCallerIdentityInjector.restoreCallingIdentity(token);
}
@@ -364,7 +372,9 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
int userId = mCallerIdentityInjector.getCallingUserId();
final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
- return mTimeZoneDetectorStrategy.suggestManualTimeZone(userId, timeZoneSuggestion);
+ final boolean bypassUserPolicyChecks = false;
+ return mTimeZoneDetectorStrategy.suggestManualTimeZone(
+ userId, timeZoneSuggestion, bypassUserPolicyChecks);
} finally {
mCallerIdentityInjector.restoreCallingIdentity(token);
}
@@ -449,7 +459,7 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
private void enforceSuggestGeolocationTimeZonePermission() {
// The associated method is only used for the shell command interface, it's not possible to
// call it via Binder, and Shell currently can set the time zone directly anyway.
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
android.Manifest.permission.SET_TIME_ZONE,
"suggest geolocation time zone");
}
@@ -461,7 +471,7 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
}
private void enforceSuggestManualTimeZonePermission() {
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE,
"suggest manual time and time zone");
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index e4b2df1460ae..69284e322663 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -120,9 +120,13 @@ public interface TimeZoneDetectorStrategy extends Dumpable {
/**
* Suggests a time zone for the device using manually-entered (i.e. user sourced) information.
+ *
+ * @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device
+ * policy restrictions that should apply to actual users can be ignored
*/
boolean suggestManualTimeZone(
- @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion);
+ @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion,
+ boolean bypassUserPolicyChecks);
/**
* Suggests a time zone for the device, or withdraws a previous suggestion if
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index 1e88c47e7b30..18c8885ee059 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -317,7 +317,8 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
@Override
public synchronized boolean suggestManualTimeZone(
- @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion) {
+ @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion,
+ boolean bypassUserPolicyChecks) {
ConfigurationInternal currentUserConfig = mCurrentConfigurationInternal;
if (currentUserConfig.getUserId() != userId) {
@@ -334,7 +335,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
String cause = "Manual time suggestion received: suggestion=" + suggestion;
TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
- currentUserConfig.createCapabilitiesAndConfig();
+ currentUserConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
if (capabilities.getSetManualTimeZoneCapability() != CAPABILITY_POSSESSED) {
Slog.i(LOG_TAG, "User does not have the capability needed to set the time zone manually"
@@ -757,7 +758,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
ipw.increaseIndent(); // level 1
ipw.println("mCurrentConfigurationInternal=" + mCurrentConfigurationInternal);
- ipw.println("[Capabilities=" + mCurrentConfigurationInternal.createCapabilitiesAndConfig()
+ final boolean bypassUserPolicyChecks = false;
+ ipw.println("[Capabilities="
+ + mCurrentConfigurationInternal.createCapabilitiesAndConfig(bypassUserPolicyChecks)
+ "]");
ipw.println("mEnvironment.getDeviceTimeZone()=" + mEnvironment.getDeviceTimeZone());
ipw.println("mEnvironment.getDeviceTimeZoneConfidence()="
diff --git a/services/core/java/com/android/server/audio/AudioEventLogger.java b/services/core/java/com/android/server/utils/EventLogger.java
index 259990ce0687..45b4ff8e1832 100644
--- a/services/core/java/com/android/server/audio/AudioEventLogger.java
+++ b/services/core/java/com/android/server/utils/EventLogger.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.audio;
+package com.android.server.utils;
import android.annotation.IntDef;
import android.util.Log;
@@ -25,24 +25,82 @@ import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
+import java.util.Locale;
-public class AudioEventLogger {
+/**
+ * Logs human-readable events for debugging purposes.
+ */
+public class EventLogger {
- // ring buffer of events to log.
- private final LinkedList<Event> mEvents;
+ /** Identifies the source of events. */
+ private final String mTag;
- private final String mTitle;
+ /** Stores the events using a ring buffer. */
+ private final LinkedList<Event> mEvents;
- // the maximum number of events to keep in log
+ /**
+ * The maximum number of events to keep in {@code mEvents}.
+ *
+ * <p>Calling {@link #log} when the size of {@link #mEvents} matches the threshold will
+ * cause the oldest event to be evicted.
+ */
private final int mMemSize;
- public static abstract class Event {
- // formatter for timestamps
- private final static SimpleDateFormat sFormat = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
+ /**
+ * Constructor for logger.
+ * @param size the maximum number of events to keep in log
+ * @param tag the string displayed before the recorded log
+ */
+ public EventLogger(int size, String tag) {
+ mEvents = new LinkedList<Event>();
+ mMemSize = size;
+ mTag = tag;
+ }
+
+ public synchronized void log(Event evt) {
+ if (mEvents.size() >= mMemSize) {
+ mEvents.removeFirst();
+ }
+ mEvents.add(evt);
+ }
+
+ /**
+ * Add a string-based event to the log, and print it to logcat as info.
+ * @param msg the message for the logs
+ * @param tag the logcat tag to use
+ */
+ public synchronized void loglogi(String msg, String tag) {
+ final Event event = new StringEvent(msg);
+ log(event.printLog(tag));
+ }
+
+ /**
+ * Same as {@link #loglogi(String, String)} but specifying the logcat type
+ * @param msg the message for the logs
+ * @param logType the type of logcat entry
+ * @param tag the logcat tag to use
+ */
+ public synchronized void loglog(String msg, @Event.LogType int logType, String tag) {
+ final Event event = new StringEvent(msg);
+ log(event.printLog(logType, tag));
+ }
+
+ public synchronized void dump(PrintWriter pw) {
+ pw.println("Events log: " + mTag);
+ for (Event evt : mEvents) {
+ pw.println(evt.toString());
+ }
+ }
+
+ public abstract static class Event {
+
+ /** Timestamps formatter. */
+ private static final SimpleDateFormat sFormat =
+ new SimpleDateFormat("MM-dd HH:mm:ss:SSS", Locale.US);
private final long mTimestamp;
- Event() {
+ public Event() {
mTimestamp = System.currentTimeMillis();
}
@@ -114,7 +172,7 @@ public class AudioEventLogger {
* Timestamp information will be automatically added, do not include it.
* @return a string representation of the event that occurred.
*/
- abstract public String eventToString();
+ public abstract String eventToString();
}
public static class StringEvent extends Event {
@@ -129,50 +187,4 @@ public class AudioEventLogger {
return mMsg;
}
}
-
- /**
- * Constructor for logger.
- * @param size the maximum number of events to keep in log
- * @param title the string displayed before the recorded log
- */
- public AudioEventLogger(int size, String title) {
- mEvents = new LinkedList<Event>();
- mMemSize = size;
- mTitle = title;
- }
-
- public synchronized void log(Event evt) {
- if (mEvents.size() >= mMemSize) {
- mEvents.removeFirst();
- }
- mEvents.add(evt);
- }
-
- /**
- * Add a string-based event to the log, and print it to logcat as info.
- * @param msg the message for the logs
- * @param tag the logcat tag to use
- */
- public synchronized void loglogi(String msg, String tag) {
- final Event event = new StringEvent(msg);
- log(event.printLog(tag));
- }
-
- /**
- * Same as {@link #loglogi(String, String)} but specifying the logcat type
- * @param msg the message for the logs
- * @param logType the type of logcat entry
- * @param tag the logcat tag to use
- */
- public synchronized void loglog(String msg, @Event.LogType int logType, String tag) {
- final Event event = new StringEvent(msg);
- log(event.printLog(logType, tag));
- }
-
- public synchronized void dump(PrintWriter pw) {
- pw.println("Audio event log: " + mTitle);
- for (Event evt : mEvents) {
- pw.println(evt.toString());
- }
- }
}
diff --git a/services/core/java/com/android/server/utils/OWNERS b/services/core/java/com/android/server/utils/OWNERS
index 62afcc86792c..be91611deccc 100644
--- a/services/core/java/com/android/server/utils/OWNERS
+++ b/services/core/java/com/android/server/utils/OWNERS
@@ -8,3 +8,5 @@ per-file Watched* = file:/services/core/java/com/android/server/pm/OWNERS
per-file Watched* = shombert@google.com
per-file Watcher.java = file:/services/core/java/com/android/server/pm/OWNERS
per-file Watcher.java = shombert@google.com
+per-file EventLogger.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS
+per-file EventLogger.java = jmtrivi@google.com
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index b473700c3074..214a2c197a5c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1481,7 +1481,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
mRootWindowContainer.getDefaultTaskDisplayArea(), currentRootTask,
forceNonResizeable);
- if (r != null) {
+ if (r != null && (options == null || !options.getDisableStartingWindow())) {
// Use a starting window to reduce the transition latency for reshowing the task.
// Note that with shell transition, this should be executed before requesting
// transition to avoid delaying the starting window.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index ac61cb940e49..442777a20281 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -28,8 +28,6 @@ import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
-import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -118,7 +116,6 @@ import android.util.ArraySet;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InsetsFlags;
@@ -1150,24 +1147,6 @@ public class DisplayPolicy {
break;
case TYPE_STATUS_BAR:
mStatusBar = win;
- final TriConsumer<DisplayFrames, WindowContainer, Rect> gestureFrameProvider =
- (displayFrames, windowContainer, rect) -> {
- rect.bottom = rect.top + getStatusBarHeight(displayFrames);
- final DisplayCutout cutout =
- displayFrames.mInsetsState.getDisplayCutout();
- if (cutout != null) {
- final Rect top = cutout.getBoundingRectTop();
- if (!top.isEmpty()) {
- rect.bottom = Math.max(rect.bottom,
- top.bottom + mDisplayCutoutTouchableRegionSize);
- }
- }
- };
- mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, null);
- mDisplayContent.setInsetProvider(
- ITYPE_TOP_MANDATORY_GESTURES, win, gestureFrameProvider);
- mDisplayContent.setInsetProvider(ITYPE_TOP_TAPPABLE_ELEMENT, win, null);
- mInsetsSourceWindowsExceptIme.add(win);
break;
case TYPE_NAVIGATION_BAR:
mNavigationBar = win;
@@ -1185,7 +1164,8 @@ public class DisplayPolicy {
displayFrames.mUnrestricted,
win.getBounds(), displayFrames.mDisplayCutoutSafe,
inOutFrame, provider.source,
- provider.insetsSize, lp.privateFlags);
+ provider.insetsSize, lp.privateFlags,
+ provider.minimalInsetsSizeInDisplayCutoutSafe);
}
}
inOutFrame.inset(win.mGivenContentInsets);
@@ -1231,85 +1211,91 @@ public class DisplayPolicy {
mInsetsSourceWindowsExceptIme.add(win);
if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
break;
- default:
- if (attrs.providedInsets != null) {
- for (int i = attrs.providedInsets.length - 1; i >= 0; i--) {
- final InsetsFrameProvider provider = attrs.providedInsets[i];
- switch (provider.type) {
- case ITYPE_STATUS_BAR:
- mStatusBarAlt = win;
- mStatusBarAltPosition = getAltBarPosition(attrs);
- break;
- case ITYPE_NAVIGATION_BAR:
- mNavigationBarAlt = win;
- mNavigationBarAltPosition = getAltBarPosition(attrs);
- break;
- case ITYPE_CLIMATE_BAR:
- mClimateBarAlt = win;
- mClimateBarAltPosition = getAltBarPosition(attrs);
- break;
- case ITYPE_EXTRA_NAVIGATION_BAR:
- mExtraNavBarAlt = win;
- mExtraNavBarAltPosition = getAltBarPosition(attrs);
- break;
+ }
+ // TODO(b/239145252): Temporarily skip the navigation bar as it is still with the hard-coded
+ // logic.
+ if (attrs.providedInsets != null && attrs.type != TYPE_NAVIGATION_BAR) {
+ for (int i = attrs.providedInsets.length - 1; i >= 0; i--) {
+ final InsetsFrameProvider provider = attrs.providedInsets[i];
+ switch (provider.type) {
+ case ITYPE_STATUS_BAR:
+ if (attrs.type != TYPE_STATUS_BAR) {
+ mStatusBarAlt = win;
+ mStatusBarAltPosition = getAltBarPosition(attrs);
}
- // The index of the provider and corresponding insets types cannot change at
- // runtime as ensured in WMS. Make use of the index in the provider directly
- // to access the latest provided size at runtime.
- final int index = i;
- final TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider =
- provider.insetsSize != null
- ? (displayFrames, windowContainer, inOutFrame) -> {
- inOutFrame.inset(win.mGivenContentInsets);
+ break;
+ case ITYPE_NAVIGATION_BAR:
+ if (attrs.type != TYPE_NAVIGATION_BAR) {
+ mNavigationBarAlt = win;
+ mNavigationBarAltPosition = getAltBarPosition(attrs);
+ }
+ break;
+ case ITYPE_CLIMATE_BAR:
+ mClimateBarAlt = win;
+ mClimateBarAltPosition = getAltBarPosition(attrs);
+ break;
+ case ITYPE_EXTRA_NAVIGATION_BAR:
+ mExtraNavBarAlt = win;
+ mExtraNavBarAltPosition = getAltBarPosition(attrs);
+ break;
+ }
+ // The index of the provider and corresponding insets types cannot change at
+ // runtime as ensured in WMS. Make use of the index in the provider directly
+ // to access the latest provided size at runtime.
+ final int index = i;
+ final TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider =
+ provider.insetsSize != null
+ ? (displayFrames, windowContainer, inOutFrame) -> {
+ inOutFrame.inset(win.mGivenContentInsets);
+ final LayoutParams lp =
+ win.mAttrs.forRotation(displayFrames.mRotation);
+ final InsetsFrameProvider ifp =
+ win.mAttrs.forRotation(displayFrames.mRotation)
+ .providedInsets[index];
+ InsetsFrameProvider.calculateInsetsFrame(
+ displayFrames.mUnrestricted,
+ windowContainer.getBounds(),
+ displayFrames.mDisplayCutoutSafe,
+ inOutFrame, ifp.source,
+ ifp.insetsSize, lp.privateFlags,
+ ifp.minimalInsetsSizeInDisplayCutoutSafe);
+ } : null;
+ final InsetsFrameProvider.InsetsSizeOverride[] overrides =
+ provider.insetsSizeOverrides;
+ final SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>>
+ overrideProviders;
+ if (overrides != null) {
+ overrideProviders = new SparseArray<>();
+ for (int j = overrides.length - 1; j >= 0; j--) {
+ final int overrideIndex = j;
+ final TriConsumer<DisplayFrames, WindowContainer, Rect>
+ overrideFrameProvider =
+ (displayFrames, windowContainer, inOutFrame) -> {
final LayoutParams lp =
- win.mAttrs.forRotation(displayFrames.mRotation);
+ win.mAttrs.forRotation(
+ displayFrames.mRotation);
final InsetsFrameProvider ifp =
- win.mAttrs.forRotation(displayFrames.mRotation)
- .providedInsets[index];
+ win.mAttrs.providedInsets[index];
InsetsFrameProvider.calculateInsetsFrame(
displayFrames.mUnrestricted,
windowContainer.getBounds(),
displayFrames.mDisplayCutoutSafe,
inOutFrame, ifp.source,
- ifp.insetsSize, lp.privateFlags);
- } : null;
- final InsetsFrameProvider.InsetsSizeOverride[] overrides =
- provider.insetsSizeOverrides;
- final SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>>
- overrideProviders;
- if (overrides != null) {
- overrideProviders = new SparseArray<>();
- for (int j = overrides.length - 1; j >= 0; j--) {
- final int overrideIndex = j;
- final TriConsumer<DisplayFrames, WindowContainer, Rect>
- overrideFrameProvider =
- (displayFrames, windowContainer, inOutFrame) -> {
- final LayoutParams lp =
- win.mAttrs.forRotation(
- displayFrames.mRotation);
- final InsetsFrameProvider ifp =
- win.mAttrs.providedInsets[index];
- InsetsFrameProvider.calculateInsetsFrame(
- displayFrames.mUnrestricted,
- windowContainer.getBounds(),
- displayFrames.mDisplayCutoutSafe,
- inOutFrame, ifp.source,
- ifp.insetsSizeOverrides[
- overrideIndex].insetsSize,
- lp.privateFlags);
- };
- overrideProviders.put(overrides[j].windowType,
- overrideFrameProvider);
- }
- } else {
- overrideProviders = null;
- }
- mDisplayContent.setInsetProvider(provider.type, win, frameProvider,
- overrideProviders);
- mInsetsSourceWindowsExceptIme.add(win);
+ ifp.insetsSizeOverrides[
+ overrideIndex].insetsSize,
+ lp.privateFlags,
+ null);
+ };
+ overrideProviders.put(overrides[j].windowType,
+ overrideFrameProvider);
}
+ } else {
+ overrideProviders = null;
}
- break;
+ mDisplayContent.setInsetProvider(provider.type, win, frameProvider,
+ overrideProviders);
+ mInsetsSourceWindowsExceptIme.add(win);
+ }
}
}
@@ -2709,7 +2695,7 @@ public class DisplayPolicy {
*
* @param screenshotType The type of screenshot, for example either
* {@link WindowManager#TAKE_SCREENSHOT_FULLSCREEN} or
- * {@link WindowManager#TAKE_SCREENSHOT_SELECTED_REGION}
+ * {@link WindowManager#TAKE_SCREENSHOT_PROVIDED_IMAGE}
* @param source Where the screenshot originated from (see WindowManager.ScreenshotSource)
*/
public void takeScreenshot(int screenshotType, int source) {
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index a638784390b6..92f7efde5799 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -265,7 +265,7 @@ public class SafeActivityOptions {
ActivityOptions options, int callingPid, int callingUid) {
// If a launch task id is specified, then ensure that the caller is the recents
// component or has the START_TASKS_FROM_RECENTS permission
- if (options.getLaunchTaskId() != INVALID_TASK_ID
+ if ((options.getLaunchTaskId() != INVALID_TASK_ID || options.getDisableStartingWindow())
&& !supervisor.mRecentTasks.isCallerRecents(callingUid)) {
final int startInTaskPerm = ActivityTaskManagerService.checkPermission(
START_TASKS_FROM_RECENTS, callingPid, callingUid);
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 24d4e981e9ce..55055390b0ff 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -56,6 +56,7 @@ import android.window.ScreenCapture;
import com.android.internal.R;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.display.DisplayControl;
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.utils.RotationAnimationUtils;
@@ -177,7 +178,7 @@ class ScreenRotationAnimation {
}
final DisplayAddress.Physical physicalAddress =
(DisplayAddress.Physical) address;
- final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(
+ final IBinder displayToken = DisplayControl.getPhysicalDisplayToken(
physicalAddress.getPhysicalDisplayId());
if (displayToken == null) {
Slog.e(TAG, "Display token is null.");
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 39ea7a896e9d..ebc8ae00cc8c 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5051,6 +5051,9 @@ class Task extends TaskFragment {
== ActivityOptions.ANIM_SCENE_TRANSITION) {
doShow = false;
}
+ if (options != null && options.getDisableStartingWindow()) {
+ doShow = false;
+ }
if (r.mLaunchTaskBehind) {
// Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
// tell WindowManager that r is visible even though it is at the back of the root
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0f5184a03e6f..36389eacf27c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3868,8 +3868,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// configuration update when the window has requested to be hidden. Doing so can lead to
// the client erroneously accepting a configuration that would have otherwise caused an
// activity restart. We instead hand back the last reported {@link MergedConfiguration}.
- if (useLatestConfig || (relayoutVisible && (!shouldCheckTokenVisibleRequested()
- || mToken.isVisibleRequested()))) {
+ if (useLatestConfig || (relayoutVisible && (mActivityRecord == null
+ || mActivityRecord.mVisibleRequested))) {
final Configuration globalConfig = getProcessGlobalConfiguration();
final Configuration overrideConfig = getMergedOverrideConfiguration();
outMergedConfiguration.setConfiguration(globalConfig, overrideConfig);
diff --git a/services/core/jni/com_android_server_display_DisplayControl.cpp b/services/core/jni/com_android_server_display_DisplayControl.cpp
index 02e5061a3ac6..185933382384 100644
--- a/services/core/jni/com_android_server_display_DisplayControl.cpp
+++ b/services/core/jni/com_android_server_display_DisplayControl.cpp
@@ -55,6 +55,28 @@ static void nativeOverrideHdrTypes(JNIEnv* env, jclass clazz, jobject tokenObjec
}
}
+static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
+ const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
+ ScopedLongArrayRW values(env, env->NewLongArray(displayIds.size()));
+ if (values.get() == nullptr) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
+ return nullptr;
+ }
+
+ for (size_t i = 0; i < displayIds.size(); ++i) {
+ values[i] = static_cast<jlong>(displayIds[i].value);
+ }
+
+ return values.getJavaArray();
+}
+
+static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
+ const auto id = DisplayId::fromValue<PhysicalDisplayId>(physicalDisplayId);
+ if (!id) return nullptr;
+ sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(*id);
+ return javaObjectForIBinder(env, token);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sDisplayMethods[] = {
@@ -65,6 +87,10 @@ static const JNINativeMethod sDisplayMethods[] = {
(void*)nativeDestroyDisplay },
{"nativeOverrideHdrTypes", "(Landroid/os/IBinder;[I)V",
(void*)nativeOverrideHdrTypes },
+ {"nativeGetPhysicalDisplayIds", "()[J",
+ (void*)nativeGetPhysicalDisplayIds },
+ {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
+ (void*)nativeGetPhysicalDisplayToken },
// clang-format on
};
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 98e5f1d3590f..b9140513d85b 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -383,6 +383,7 @@
</xs:complexType>
<xs:complexType name="autoBrightness">
+ <xs:attribute name="enabled" type="xs:boolean" use="optional" default="true"/>
<xs:sequence>
<!-- Sets the debounce for autoBrightness brightening in millis-->
<xs:element name="brighteningLightDebounceMillis" type="xs:nonNegativeInteger"
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 748ef4be9cc7..d89bd7cc9aa2 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -6,9 +6,11 @@ package com.android.server.display.config {
method public final java.math.BigInteger getBrighteningLightDebounceMillis();
method public final java.math.BigInteger getDarkeningLightDebounceMillis();
method public final com.android.server.display.config.DisplayBrightnessMapping getDisplayBrightnessMapping();
+ method public boolean getEnabled();
method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
method public final void setDisplayBrightnessMapping(com.android.server.display.config.DisplayBrightnessMapping);
+ method public void setEnabled(boolean);
}
public class BrightnessThresholds {
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayBrightnessStateTest.java
new file mode 100644
index 000000000000..50996d7199c8
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayBrightnessStateTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.display.brightness.BrightnessReason;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DisplayBrightnessStateTest {
+ private static final float FLOAT_DELTA = 0.001f;
+
+ private DisplayBrightnessState.Builder mDisplayBrightnessStateBuilder;
+
+ @Before
+ public void before() {
+ mDisplayBrightnessStateBuilder = new DisplayBrightnessState.Builder();
+ }
+
+ @Test
+ public void validateAllDisplayBrightnessStateFieldsAreSetAsExpected() {
+ float brightness = 0.3f;
+ float sdrBrightness = 0.2f;
+ BrightnessReason brightnessReason = new BrightnessReason();
+ brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC);
+ brightnessReason.setModifier(BrightnessReason.MODIFIER_DIMMED);
+ DisplayBrightnessState displayBrightnessState =
+ mDisplayBrightnessStateBuilder.setBrightness(brightness).setSdrBrightness(
+ sdrBrightness).setBrightnessReason(brightnessReason).build();
+
+ assertEquals(displayBrightnessState.getBrightness(), brightness, FLOAT_DELTA);
+ assertEquals(displayBrightnessState.getSdrBrightness(), sdrBrightness, FLOAT_DELTA);
+ assertEquals(displayBrightnessState.getBrightnessReason(), brightnessReason);
+ assertEquals(displayBrightnessState.toString(), getString(displayBrightnessState));
+ }
+
+ private String getString(DisplayBrightnessState displayBrightnessState) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("DisplayBrightnessState:");
+ sb.append("\n brightness:" + displayBrightnessState.getBrightness());
+ sb.append("\n sdrBrightness:" + displayBrightnessState.getSdrBrightness());
+ sb.append("\n brightnessReason:" + displayBrightnessState.getBrightnessReason());
+ return sb.toString();
+ }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 1921ce70d6cd..ebdef8af47f8 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -48,7 +48,6 @@ android_test {
"ShortcutManagerTestUtils",
"truth-prebuilt",
"testables",
- "testng",
"ub-uiautomator",
"platformprotosnano",
"framework-protos",
@@ -60,6 +59,7 @@ android_test {
// which provides assertThrows
"testng",
"junit",
+ "junit-params",
"platform-compat-test-rules",
"ActivityContext",
"coretests-aidl",
diff --git a/services/tests/servicestests/res/xml/irq_device_map_1.xml b/services/tests/servicestests/res/xml/irq_device_map_1.xml
new file mode 100644
index 000000000000..1f1a77b437ab
--- /dev/null
+++ b/services/tests/servicestests/res/xml/irq_device_map_1.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2022, 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.
+*/
+-->
+<irq-device-map>
+ <device name="test.device.1">
+ <subsystem>test.subsystem.1</subsystem>
+ <subsystem>test.subsystem.2</subsystem>
+ </device>
+ <device name="test.device.2">
+ <subsystem>test.subsystem.3</subsystem>
+ <subsystem>test.subsystem.2</subsystem>
+ </device>
+</irq-device-map> \ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/irq_device_map_2.xml b/services/tests/servicestests/res/xml/irq_device_map_2.xml
new file mode 100644
index 000000000000..508c98d871da
--- /dev/null
+++ b/services/tests/servicestests/res/xml/irq_device_map_2.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2022, 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.
+*/
+-->
+
+<!-- Long comment describing anything that may be needed
+for this file and its maintenance -->
+<irq-device-map>
+ <!-- Small comment specific to this device -->
+ <invalid name="test.device.1">
+ <!-- These valid subsystem definitions should be ignored because of invalid parent tag -->
+ <subsystem>test.subsystem.1</subsystem>
+ <subsystem>test.subsystem.2</subsystem>
+ </invalid>
+ <device name="test.device.2">
+ <!-- Multiline comment to describe nuances
+ about why these subsystems
+ rely on this hardware device
+ to wake the CPU up from sleep
+ -->
+ <subsystem>test.subsystem.3</subsystem>
+ <!-- Small comment specific to test.subsystem.4 -->
+ <subsystem>test.subsystem.4</subsystem>
+ <subsystem>test.subsystem.5</subsystem>
+ <!-- Duplicates should be ignored -->
+ <subsystem>test.subsystem.4</subsystem>
+ <subsystem>test.subsystem.3</subsystem>
+ <subsystem>test.subsystem.5</subsystem>
+ </device>
+
+ <device name="test.device.3">
+ <!-- All child tags are invalid, mapping should be empty / non-existent for this device -->
+ <subsys>ignored</subsys>
+ <system>redundant</system>
+ </device>
+
+ <device name="test.device.4">
+ <!-- Invalid child tags should be skipped but others should be mapped -->
+ <invalid>unused</invalid>
+ <random>skipped</random>
+ <subsystem>test.subsystem.1</subsystem>
+ <subsystem>test.subsystem.4</subsystem>
+ </device>
+
+</irq-device-map> \ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/irq_device_map_3.xml b/services/tests/servicestests/res/xml/irq_device_map_3.xml
new file mode 100644
index 000000000000..498b676dd1dc
--- /dev/null
+++ b/services/tests/servicestests/res/xml/irq_device_map_3.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2022, 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.
+*/
+-->
+<irq-device-map>
+ <device name="test.alarm.device">
+ <subsystem>Alarm</subsystem>
+ </device>
+ <device name="test.wifi.device">
+ <subsystem>undefined</subsystem>
+ </device>
+</irq-device-map> \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 2d2c76c40b10..0b776a3e6642 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -181,11 +181,8 @@ public class UserControllerTest {
doNothing().when(mInjector).taskSupervisorRemoveUser(anyInt());
mockIsUsersOnSecondaryDisplaysEnabled(false);
// All UserController params are set to default.
- mUserController = new UserController(mInjector);
- // TODO(b/232452368): need to explicitly call setAllowUserUnlocking(), otherwise most
- // tests would fail. But we might need to disable it for the onBootComplete() test (i.e,
- // to make sure the users are unlocked at the right time)
+ mUserController = new UserController(mInjector);
mUserController.setAllowUserUnlocking(true);
setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS);
setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated= */ true, null);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 6388c7d52f10..9c5d1a5b0610 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -226,7 +226,7 @@ public class VirtualDeviceManagerServiceTest {
mContext.getSystemService(WindowManager.class), threadVerifier);
mAssociationInfo = new AssociationInfo(1, 0, null,
- MacAddress.BROADCAST_ADDRESS, "", null, true, false, false, 0, 0);
+ MacAddress.BROADCAST_ADDRESS, "", null, null, true, false, false, 0, 0);
mVdms = new VirtualDeviceManagerService(mContext);
mLocalService = mVdms.getLocalServiceInstance();
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 3c7bb2ac51d6..6860abf40b56 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -129,19 +130,43 @@ public class DisplayManagerServiceTest {
}
@Override
+ LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
+ Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ return new LocalDisplayAdapter(syncRoot, context, handler,
+ displayAdapterListener, new LocalDisplayAdapter.Injector() {
+ @Override
+ public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
+ return mSurfaceControlProxy;
+ }
+ });
+ }
+
+ @Override
long getDefaultDisplayDelayTimeout() {
return SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS;
}
};
- class BasicInjector extends DisplayManagerService.Injector {
- @Override
- VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
- return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
- (String name, boolean secure) -> mMockDisplayToken);
- }
- }
+ class BasicInjector extends DisplayManagerService.Injector {
+ @Override
+ VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
+ Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+ (String name, boolean secure) -> mMockDisplayToken);
+ }
+
+ @Override
+ LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
+ Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ return new LocalDisplayAdapter(syncRoot, context, handler,
+ displayAdapterListener, new LocalDisplayAdapter.Injector() {
+ @Override
+ public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
+ return mSurfaceControlProxy;
+ }
+ });
+ }
+ }
private final DisplayManagerService.Injector mBasicInjector = new BasicInjector();
@@ -168,6 +193,7 @@ public class DisplayManagerServiceTest {
@Mock WindowManagerInternal mMockWindowManagerInternal;
@Mock LightsManager mMockLightsManager;
@Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter;
+ @Mock LocalDisplayAdapter.SurfaceControlProxy mSurfaceControlProxy;
@Mock IBinder mMockDisplayToken;
@Mock SensorManagerInternal mMockSensorManagerInternal;
@@ -191,6 +217,28 @@ public class DisplayManagerServiceTest {
// Disable binder caches in this process.
PropertyInvalidatedCache.disableForTestMode();
+ setUpDisplay();
+ }
+
+ private void setUpDisplay() {
+ long[] ids = new long[] {100};
+ when(mSurfaceControlProxy.getPhysicalDisplayIds()).thenReturn(ids);
+ when(mSurfaceControlProxy.getPhysicalDisplayToken(anyLong()))
+ .thenReturn(mMockDisplayToken);
+ SurfaceControl.StaticDisplayInfo staticDisplayInfo = new SurfaceControl.StaticDisplayInfo();
+ staticDisplayInfo.isInternal = true;
+ when(mSurfaceControlProxy.getStaticDisplayInfo(mMockDisplayToken))
+ .thenReturn(staticDisplayInfo);
+ SurfaceControl.DynamicDisplayInfo dynamicDisplayMode =
+ new SurfaceControl.DynamicDisplayInfo();
+ SurfaceControl.DisplayMode displayMode = new SurfaceControl.DisplayMode();
+ displayMode.width = 100;
+ displayMode.height = 200;
+ dynamicDisplayMode.supportedDisplayModes = new SurfaceControl.DisplayMode[] {displayMode};
+ when(mSurfaceControlProxy.getDynamicDisplayInfo(mMockDisplayToken))
+ .thenReturn(dynamicDisplayMode);
+ when(mSurfaceControlProxy.getDesiredDisplayModeSpecs(mMockDisplayToken))
+ .thenReturn(new SurfaceControl.DesiredDisplayModeSpecs());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt b/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
index 5f3f3d7714ef..65076a372b0b 100644
--- a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
@@ -33,6 +33,7 @@ import android.platform.test.annotations.Presubmit
import android.view.InputDevice
import androidx.test.InstrumentationRegistry
import com.android.server.input.BatteryController.UEventManager
+import com.android.server.input.BatteryController.UEventManager.UEventBatteryListener
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.MatcherAssert.assertThat
@@ -260,25 +261,28 @@ class BatteryControllerTests {
@Test
fun testListenersNotifiedOnUEventNotification() {
- `when`(native.getBatteryDevicePath(DEVICE_ID)).thenReturn("/test/device1")
+ `when`(native.getBatteryDevicePath(DEVICE_ID)).thenReturn("/sys/dev/test/device1")
`when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
val listener = createMockListener()
- val uEventListener = ArgumentCaptor.forClass(UEventManager.UEventListener::class.java)
+ val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
- verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/test/device1"))
+ // The device paths for UEvent notifications do not include the "/sys" prefix, so verify
+ // that the added listener is configured to match the path without that prefix.
+ verify(uEventManager)
+ .addListener(uEventListener.capture(), eq("DEVPATH=/dev/test/device1"))
listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f)
// If the battery state has changed when an UEvent is sent, the listeners are notified.
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
- uEventListener.value!!.onUEvent(TIMESTAMP)
+ uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.80f,
eventTime = TIMESTAMP)
// If the battery state has not changed when an UEvent is sent, the listeners are not
// notified.
clearInvocations(listener)
- uEventListener.value!!.onUEvent(TIMESTAMP + 1)
+ uEventListener.value!!.onBatteryUEvent(TIMESTAMP + 1)
verifyNoMoreInteractions(listener)
batteryController.unregisterBatteryListener(DEVICE_ID, listener, PID)
@@ -293,7 +297,7 @@ class BatteryControllerTests {
`when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
val listener = createMockListener()
- val uEventListener = ArgumentCaptor.forClass(UEventManager.UEventListener::class.java)
+ val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/test/device1"))
listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f)
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 5e6cccc07983..1e67c120b6e0 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -75,15 +75,34 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
@Test
- public void testLskfBasedProtector() throws RemoteException {
+ public void testNoneLskfBasedProtector() throws RemoteException {
+ final int USER_ID = 10;
+ MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage,
+ mGateKeeperService, mUserManager, mPasswordSlotManager);
+ SyntheticPassword sp = manager.newSyntheticPassword(USER_ID);
+ assertFalse(lskfGatekeeperHandleExists(USER_ID));
+ long protectorId = manager.createLskfBasedProtector(mGateKeeperService,
+ LockscreenCredential.createNone(), sp, USER_ID);
+ assertFalse(lskfGatekeeperHandleExists(USER_ID));
+
+ AuthenticationResult result = manager.unlockLskfBasedProtector(mGateKeeperService,
+ protectorId, LockscreenCredential.createNone(), USER_ID, null);
+ assertArrayEquals(result.syntheticPassword.deriveKeyStorePassword(),
+ sp.deriveKeyStorePassword());
+ }
+
+ @Test
+ public void testNonNoneLskfBasedProtector() throws RemoteException {
final int USER_ID = 10;
final LockscreenCredential password = newPassword("user-password");
final LockscreenCredential badPassword = newPassword("bad-password");
MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage,
mGateKeeperService, mUserManager, mPasswordSlotManager);
SyntheticPassword sp = manager.newSyntheticPassword(USER_ID);
+ assertFalse(lskfGatekeeperHandleExists(USER_ID));
long protectorId = manager.createLskfBasedProtector(mGateKeeperService, password, sp,
USER_ID);
+ assertTrue(lskfGatekeeperHandleExists(USER_ID));
AuthenticationResult result = manager.unlockLskfBasedProtector(mGateKeeperService,
protectorId, password, USER_ID, null);
@@ -95,6 +114,10 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
assertNull(result.syntheticPassword);
}
+ private boolean lskfGatekeeperHandleExists(int userId) throws RemoteException {
+ return mGateKeeperService.getSecureUserId(SyntheticPasswordManager.fakeUserId(userId)) != 0;
+ }
+
private boolean hasSyntheticPassword(int userId) throws RemoteException {
return mService.getLong(CURRENT_LSKF_BASED_PROTECTOR_ID_KEY, 0, userId) != 0;
}
@@ -430,6 +453,21 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
@Test
+ public void testPasswordData_scryptParams() {
+ // CREDENTIAL_TYPE_NONE should result in the minimum scrypt params being used.
+ PasswordData data = PasswordData.create(CREDENTIAL_TYPE_NONE);
+ assertEquals(1, data.scryptLogN);
+ assertEquals(0, data.scryptLogR);
+ assertEquals(0, data.scryptLogP);
+
+ // Any other credential type should result in the real scrypt params being used.
+ data = PasswordData.create(CREDENTIAL_TYPE_PASSWORD);
+ assertTrue(data.scryptLogN > 1);
+ assertTrue(data.scryptLogR > 0);
+ assertTrue(data.scryptLogP > 0);
+ }
+
+ @Test
public void testPasswordData_serializeDeserialize() {
PasswordData data = new PasswordData();
data.scryptLogN = 11;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index f3ac246d12e4..88932b06db82 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -64,6 +64,7 @@ import com.android.server.LocalServices;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.LegacyPermissionDataProvider;
+import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SuspendParams;
@@ -657,6 +658,7 @@ public class PackageManagerSettingsTests {
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
UUID.randomUUID());
+ origPkgSetting01.setPkg(mockAndroidPackage(origPkgSetting01));
final PackageSetting testPkgSetting01 = new PackageSetting(origPkgSetting01);
verifySettingCopy(origPkgSetting01, testPkgSetting01);
}
@@ -698,6 +700,7 @@ public class PackageManagerSettingsTests {
.build();
origPkgSetting01.modifyUserState(0).putSuspendParams("suspendingPackage1",
new SuspendParams(dialogInfo1, appExtras1, launcherExtras1));
+ origPkgSetting01.setPkg(mockAndroidPackage(origPkgSetting01));
final PackageSetting testPkgSetting01 = new PackageSetting(
PACKAGE_NAME /*pkgName*/,
REAL_PACKAGE_NAME /*realPkgName*/,
@@ -719,6 +722,8 @@ public class PackageManagerSettingsTests {
testPkgSetting01.copyPackageSetting(origPkgSetting01, true);
verifySettingCopy(origPkgSetting01, testPkgSetting01);
verifyUserStatesCopy(origPkgSetting01.readUserState(0),
+ origPkgSetting01.getStateForUser(UserHandle.of(0)));
+ verifyUserStatesCopy(origPkgSetting01.readUserState(0),
testPkgSetting01.readUserState(0));
}
@@ -749,7 +754,9 @@ public class PackageManagerSettingsTests {
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
UUID.randomUUID());
+ assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getPrimaryCpuAbiLegacy(), is("arm64-v8a"));
+ assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
assertThat(testPkgSetting01.getSecondaryCpuAbiLegacy(), is("armeabi"));
assertThat(testPkgSetting01.getFlags(), is(0));
assertThat(testPkgSetting01.getPrivateFlags(), is(0));
@@ -785,10 +792,14 @@ public class PackageManagerSettingsTests {
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
UUID.randomUUID());
+ assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getPrimaryCpuAbiLegacy(), is("arm64-v8a"));
+ assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
assertThat(testPkgSetting01.getSecondaryCpuAbiLegacy(), is("armeabi"));
assertThat(testPkgSetting01.getFlags(), is(ApplicationInfo.FLAG_SYSTEM));
+ assertThat(testPkgSetting01.isSystem(), is(true));
assertThat(testPkgSetting01.getPrivateFlags(), is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
+ assertThat(testPkgSetting01.isPrivileged(), is(true));
final PackageUserState userState = testPkgSetting01.readUserState(0);
verifyUserState(userState, false /*notLaunched*/,
false /*stopped*/, true /*installed*/);
@@ -859,8 +870,12 @@ public class PackageManagerSettingsTests {
assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
assertThat(testPkgSetting01.getFlags(), is(ApplicationInfo.FLAG_SYSTEM));
+ assertThat(testPkgSetting01.isSystem(), is(true));
assertThat(testPkgSetting01.getPrivateFlags(), is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
+ assertThat(testPkgSetting01.isPrivileged(), is(true));
+ assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getPrimaryCpuAbiLegacy(), is("arm64-v8a"));
+ assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
assertThat(testPkgSetting01.getSecondaryCpuAbiLegacy(), is("armeabi"));
// signatures object must be different
assertNotSame(testPkgSetting01.getSignatures(), originalSignatures);
@@ -901,6 +916,7 @@ public class PackageManagerSettingsTests {
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
assertThat(testPkgSetting01.getFlags(), is(0));
assertThat(testPkgSetting01.getPrivateFlags(), is(0));
+ assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("x86_64"));
assertThat(testPkgSetting01.getPrimaryCpuAbiLegacy(), is("x86_64"));
assertThat(testPkgSetting01.getSecondaryCpuAbiLegacy(), is("x86"));
assertThat(testPkgSetting01.getVersionCode(), is(INITIAL_VERSION_CODE));
@@ -944,7 +960,9 @@ public class PackageManagerSettingsTests {
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
assertThat(testPkgSetting01.getFlags(), is(0));
assertThat(testPkgSetting01.getPrivateFlags(), is(0));
+ assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("x86_64"));
assertThat(testPkgSetting01.getPrimaryCpuAbiLegacy(), is("x86_64"));
+ assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("x86"));
assertThat(testPkgSetting01.getSecondaryCpuAbiLegacy(), is("x86"));
assertThat(testPkgSetting01.getVersionCode(), is(INITIAL_VERSION_CODE));
final PackageUserState userState = testPkgSetting01.readUserState(0);
@@ -987,7 +1005,9 @@ public class PackageManagerSettingsTests {
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
assertThat(testPkgSetting01.getFlags(), is(0));
assertThat(testPkgSetting01.getPrivateFlags(), is(0));
+ assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getPrimaryCpuAbiLegacy(), is("arm64-v8a"));
+ assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
assertThat(testPkgSetting01.getSecondaryCpuAbiLegacy(), is("armeabi"));
assertNotSame(testPkgSetting01.getSignatures(), disabledSignatures);
assertThat(testPkgSetting01.getVersionCode(), is(UPDATED_VERSION_CODE));
@@ -1207,13 +1227,20 @@ public class PackageManagerSettingsTests {
// assertNotSame(origPkgSetting.mOldCodePaths, testPkgSetting.mOldCodePaths);
// assertThat(origPkgSetting.mOldCodePaths, is(not(testPkgSetting.mOldCodePaths)));
assertSame(origPkgSetting.getPkg(), testPkgSetting.getPkg());
+ assertSame(origPkgSetting.getAndroidPackage(), origPkgSetting.getPkg());
+ assertThat(origPkgSetting.getAndroidPackage().getPackageName(),
+ is(origPkgSetting.getPackageName()));
// No equals() method for this object
// assertThat(origPkgSetting.pkg, is(testPkgSetting.pkg));
assertThat(origPkgSetting.getFlags(), is(testPkgSetting.getFlags()));
assertThat(origPkgSetting.getPrivateFlags(), is(testPkgSetting.getPrivateFlags()));
+ assertSame(origPkgSetting.getPrimaryCpuAbi(), testPkgSetting.getPrimaryCpuAbi());
+ assertThat(origPkgSetting.getPrimaryCpuAbi(), is(testPkgSetting.getPrimaryCpuAbi()));
assertSame(origPkgSetting.getPrimaryCpuAbiLegacy(), testPkgSetting.getPrimaryCpuAbiLegacy());
assertThat(origPkgSetting.getPrimaryCpuAbiLegacy(), is(testPkgSetting.getPrimaryCpuAbiLegacy()));
assertThat(origPkgSetting.getRealName(), is(testPkgSetting.getRealName()));
+ assertSame(origPkgSetting.getSecondaryCpuAbi(), testPkgSetting.getSecondaryCpuAbi());
+ assertThat(origPkgSetting.getSecondaryCpuAbi(), is(testPkgSetting.getSecondaryCpuAbi()));
assertSame(origPkgSetting.getSecondaryCpuAbiLegacy(), testPkgSetting.getSecondaryCpuAbiLegacy());
assertThat(origPkgSetting.getSecondaryCpuAbiLegacy(), is(testPkgSetting.getSecondaryCpuAbiLegacy()));
assertSame(origPkgSetting.getSignatures(), testPkgSetting.getSignatures());
@@ -1241,6 +1268,11 @@ public class PackageManagerSettingsTests {
origSuspendParams.getAppExtras(), testSuspendParams.getAppExtras()), is(true));
assertThat(BaseBundle.kindofEquals(origSuspendParams.getLauncherExtras(),
testSuspendParams.getLauncherExtras()), is(true));
+ }
+
+ private void verifyUserStatesCopy(PackageUserState origPus,
+ PackageUserState testPus) {
+ assertThat(userStateEquals(origPus, testPus), is(true));
// Verify that disabledComponents and enabledComponents are copied
assertThat(origPus.getDisabledComponents(), is(notNullValue()));
assertThat(origPus.getDisabledComponents().equals(testPus.getDisabledComponents()),
@@ -1573,4 +1605,9 @@ public class PackageManagerSettingsTests {
assertThat(ps.getKeySetData().getUpgradeKeySets().length, is(1));
assertThat(ps.getKeySetData().getUpgradeKeySets()[0], is(3L));
}
+
+ @NonNull
+ private AndroidPackage mockAndroidPackage(PackageSetting pkgSetting) {
+ return PackageImpl.forTesting(pkgSetting.getPackageName()).hideAsParsed().hideAsFinal();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index f6852d8117f1..e0086d0165fd 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -800,7 +800,7 @@ public class PowerManagerServiceTest {
doAnswer(inv -> {
when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
return null;
- }).when(mDreamManagerInternalMock).startDream(anyBoolean());
+ }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString());
setMinimumScreenOffTimeoutConfig(5);
createService();
@@ -822,7 +822,7 @@ public class PowerManagerServiceTest {
doAnswer(inv -> {
when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
return null;
- }).when(mDreamManagerInternalMock).startDream(anyBoolean());
+ }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString());
setMinimumScreenOffTimeoutConfig(5);
createService();
@@ -1271,7 +1271,7 @@ public class PowerManagerServiceTest {
doAnswer(inv -> {
when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
return null;
- }).when(mDreamManagerInternalMock).startDream(anyBoolean());
+ }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString());
final String pkg = mContextSpy.getOpPackageName();
final Binder token = new Binder();
@@ -1767,7 +1767,7 @@ public class PowerManagerServiceTest {
forceDozing();
// Allow handleSandman() to be called asynchronously
advanceTime(500);
- verify(mDreamManagerInternalMock).startDream(eq(true));
+ verify(mDreamManagerInternalMock).startDream(eq(true), anyString());
}
@Test
@@ -1805,7 +1805,7 @@ public class PowerManagerServiceTest {
// Allow handleSandman() to be called asynchronously
advanceTime(500);
- verify(mDreamManagerInternalMock).startDream(eq(true));
+ verify(mDreamManagerInternalMock).startDream(eq(true), anyString());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
index 0a1e3c7032ed..e9c5b01dbe3a 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
@@ -47,6 +47,7 @@ import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.util.Log;
import android.util.MutableInt;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
@@ -83,6 +84,7 @@ import java.util.function.IntConsumer;
* com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
*/
public class BatteryStatsNoteTest extends TestCase {
+ private static final String TAG = BatteryStatsNoteTest.class.getSimpleName();
private static final int UID = 10500;
private static final int ISOLATED_APP_ID = Process.FIRST_ISOLATED_UID + 23;
@@ -2030,6 +2032,113 @@ public class BatteryStatsNoteTest extends TestCase {
noRadioProcFlags, lastProcStateChangeFlags.value);
}
+ @SmallTest
+ public void testNoteMobileRadioPowerStateLocked() {
+ long curr;
+ boolean update;
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
+ final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ bi.setOnBatteryInternal(true);
+
+ // Note mobile radio is on.
+ curr = 1000L * (clocks.realtime = clocks.uptime = 1001);
+ bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, curr,
+ UID);
+
+ // Note mobile radio is still on.
+ curr = 1000L * (clocks.realtime = clocks.uptime = 2001);
+ update = bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+ curr, UID);
+ assertFalse(
+ "noteMobileRadioPowerStateLocked should not request an update when the power "
+ + "state does not change from HIGH.",
+ update);
+
+ // Note mobile radio is off.
+ curr = 1000L * (clocks.realtime = clocks.uptime = 3001);
+ update = bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+ curr, UID);
+ assertTrue(
+ "noteMobileRadioPowerStateLocked should request an update when the power state "
+ + "changes from HIGH to LOW.",
+ update);
+
+ // Note mobile radio is still off.
+ curr = 1000L * (clocks.realtime = clocks.uptime = 4001);
+ update = bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+ curr, UID);
+ assertFalse(
+ "noteMobileRadioPowerStateLocked should not request an update when the power "
+ + "state does not change from LOW.",
+ update);
+
+ // Note mobile radio is on.
+ curr = 1000L * (clocks.realtime = clocks.uptime = 5001);
+ update = bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+ curr, UID);
+ assertFalse(
+ "noteMobileRadioPowerStateLocked should not request an update when the power "
+ + "state changes from LOW to HIGH.",
+ update);
+ }
+
+ @SmallTest
+ public void testNoteMobileRadioPowerStateLocked_rateLimited() {
+ long curr;
+ boolean update;
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
+ final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ bi.setPowerProfile(mock(PowerProfile.class));
+
+ final int txLevelCount = CellSignalStrength.getNumSignalStrengthLevels();
+ final ModemActivityInfo mai = new ModemActivityInfo(0L, 0L, 0L, new int[txLevelCount], 0L);
+
+ final long rateLimit = bi.getMobileRadioPowerStateUpdateRateLimit();
+ if (rateLimit < 0) {
+ Log.w(TAG, "Skipping testNoteMobileRadioPowerStateLocked_rateLimited, rateLimit = "
+ + rateLimit);
+ return;
+ }
+ bi.setOnBatteryInternal(true);
+
+ // Note mobile radio is on.
+ curr = 1000L * (clocks.realtime = clocks.uptime = 1001);
+ bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, curr,
+ UID);
+
+ clocks.realtime = clocks.uptime = 2001;
+ mai.setTimestamp(clocks.realtime);
+ bi.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE,
+ clocks.realtime, clocks.uptime, mNetworkStatsManager);
+
+ // Note mobile radio is off within the rate limit duration.
+ clocks.realtime = clocks.uptime = clocks.realtime + (long) (rateLimit * 0.7);
+ curr = 1000L * clocks.realtime;
+ update = bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+ curr, UID);
+ assertFalse(
+ "noteMobileRadioPowerStateLocked should not request an update when the power "
+ + "state so soon after a noteModemControllerActivity",
+ update);
+
+ // Note mobile radio is on.
+ clocks.realtime = clocks.uptime = clocks.realtime + (long) (rateLimit * 0.7);
+ curr = 1000L * clocks.realtime;
+ bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, curr,
+ UID);
+
+ // Note mobile radio is off much later
+ clocks.realtime = clocks.uptime = clocks.realtime + rateLimit;
+ curr = 1000L * clocks.realtime;
+ update = bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+ curr, UID);
+ assertTrue(
+ "noteMobileRadioPowerStateLocked should request an update when the power state "
+ + "changes from HIGH to LOW much later after a "
+ + "noteModemControllerActivity.",
+ update);
+ }
+
private void setFgState(int uid, boolean fgOn, MockBatteryStatsImpl bi) {
// Note that noteUidProcessStateLocked uses ActivityManager process states.
if (fgOn) {
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
new file mode 100644
index 000000000000..7731a326cf61
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 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.stats;
+
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
+
+import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS;
+import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_RETENTION_MS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.frameworks.servicestests.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
+
+@RunWith(AndroidJUnit4.class)
+public class CpuWakeupStatsTest {
+ private static final String KERNEL_REASON_ALARM_IRQ = "120 test.alarm.device";
+ private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device";
+ private static final String KERNEL_REASON_UNKNOWN = "unsupported-free-form-reason";
+
+ private static final int TEST_UID_1 = 13239823;
+ private static final int TEST_UID_2 = 25268423;
+ private static final int TEST_UID_3 = 92261423;
+ private static final int TEST_UID_4 = 56926423;
+ private static final int TEST_UID_5 = 76421423;
+
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+ private final ThreadLocalRandom mRandom = ThreadLocalRandom.current();
+
+ @Test
+ public void removesOldWakeups() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_1);
+
+ final Set<Long> timestamps = new HashSet<>();
+ final long firstWakeup = 453192;
+
+ obj.noteWakeupTimeAndReason(firstWakeup, 32, "unused");
+ timestamps.add(firstWakeup);
+ for (int i = 1; i < 1000; i++) {
+ final long delta = mRandom.nextLong(WAKEUP_RETENTION_MS);
+ if (timestamps.add(firstWakeup + delta)) {
+ obj.noteWakeupTimeAndReason(firstWakeup + delta, i, "unused");
+ }
+ }
+ assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
+
+ obj.noteWakeupTimeAndReason(firstWakeup + WAKEUP_RETENTION_MS + 1242, 231, "unused");
+ assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
+
+ for (int i = 0; i < 100; i++) {
+ final long now = mRandom.nextLong(WAKEUP_RETENTION_MS + 1, 100 * WAKEUP_RETENTION_MS);
+ obj.noteWakeupTimeAndReason(now, i, "unused");
+ assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - WAKEUP_RETENTION_MS))
+ .isLessThan(0);
+ }
+ }
+
+ @Test
+ public void alarmIrqAttributionSolo() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+ final long wakeupTime = 12423121;
+
+ obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_ALARM_IRQ);
+
+ // Outside the window, so should be ignored.
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
+ wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
+ wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+ // Should be attributed
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3, TEST_UID_5);
+
+ final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ assertThat(attribution).isNotNull();
+ assertThat(attribution.size()).isEqualTo(1);
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
+ }
+
+ @Test
+ public void alarmIrqAttributionCombined() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+ final long wakeupTime = 92123210;
+
+ obj.noteWakeupTimeAndReason(wakeupTime, 4,
+ KERNEL_REASON_UNKNOWN_IRQ + ":" + KERNEL_REASON_ALARM_IRQ);
+
+ // Outside the window, so should be ignored.
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
+ wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
+ wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+ // Should be attributed
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4,
+ TEST_UID_5);
+
+ final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ assertThat(attribution).isNotNull();
+ assertThat(attribution.size()).isEqualTo(2);
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(true);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
+ }
+
+ @Test
+ public void unknownIrqAttribution() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+ final long wakeupTime = 92123410;
+
+ obj.noteWakeupTimeAndReason(wakeupTime, 24, KERNEL_REASON_UNKNOWN_IRQ);
+
+ // Unrelated subsystems, should not be attributed
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4,
+ TEST_UID_5);
+
+ final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ assertThat(attribution).isNotNull();
+ assertThat(attribution.size()).isEqualTo(1);
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
+ final SparseBooleanArray uids = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN);
+ assertThat(uids == null || uids.size() == 0).isTrue();
+ }
+
+ @Test
+ public void unknownAttribution() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+ final long wakeupTime = 72123210;
+
+ obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNKNOWN);
+
+ // Unrelated subsystems, should be ignored.
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4);
+
+ // There should be nothing in the attribution map.
+ assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java b/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java
new file mode 100644
index 000000000000..43d9e60c4a2b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 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.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.frameworks.servicestests.R;
+import com.android.internal.util.CollectionUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class IrqDeviceMapTest {
+ private static final String TEST_DEVICE_1 = "test.device.1";
+ private static final String TEST_DEVICE_2 = "test.device.2";
+ private static final String TEST_DEVICE_3 = "test.device.3";
+ private static final String TEST_DEVICE_4 = "test.device.4";
+
+ private static final String TEST_SUBSYSTEM_1 = "test.subsystem.1";
+ private static final String TEST_SUBSYSTEM_2 = "test.subsystem.2";
+ private static final String TEST_SUBSYSTEM_3 = "test.subsystem.3";
+ private static final String TEST_SUBSYSTEM_4 = "test.subsystem.4";
+ private static final String TEST_SUBSYSTEM_5 = "test.subsystem.5";
+
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+
+ @Test
+ public void cachesInstancesPerXml() {
+ IrqDeviceMap irqDeviceMap1 = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_1);
+ IrqDeviceMap irqDeviceMap2 = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_1);
+ assertThat(irqDeviceMap1).isSameInstanceAs(irqDeviceMap2);
+
+ irqDeviceMap2 = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_2);
+ assertThat(irqDeviceMap1).isNotSameInstanceAs(irqDeviceMap2);
+
+ irqDeviceMap1 = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_2);
+ assertThat(irqDeviceMap1).isSameInstanceAs(irqDeviceMap2);
+ }
+
+ @Test
+ public void simpleXml() {
+ IrqDeviceMap deviceMap = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_1);
+
+ List<String> subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_1);
+ assertThat(subsystems).hasSize(2);
+ // No specific order is required.
+ assertThat(subsystems).containsExactly(TEST_SUBSYSTEM_2, TEST_SUBSYSTEM_1);
+
+ subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_2);
+ assertThat(subsystems).hasSize(2);
+ // No specific order is required.
+ assertThat(subsystems).containsExactly(TEST_SUBSYSTEM_2, TEST_SUBSYSTEM_3);
+ }
+
+ @Test
+ public void complexXml() {
+ IrqDeviceMap deviceMap = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_2);
+
+ List<String> subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_1);
+ assertThat(CollectionUtils.isEmpty(subsystems)).isTrue();
+
+ subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_2);
+ assertThat(subsystems).hasSize(3);
+ // No specific order is required.
+ assertThat(subsystems).containsExactly(TEST_SUBSYSTEM_3, TEST_SUBSYSTEM_4,
+ TEST_SUBSYSTEM_5);
+
+ subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_3);
+ assertThat(CollectionUtils.isEmpty(subsystems)).isTrue();
+
+ subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_4);
+ assertThat(subsystems).hasSize(2);
+ // No specific order is required.
+ assertThat(subsystems).containsExactly(TEST_SUBSYSTEM_4, TEST_SUBSYSTEM_1);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 570b2ee617f5..df4b8962630f 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -113,6 +113,10 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
return getUidStatsLocked(uid).mOnBatteryScreenOffBackgroundTimeBase;
}
+ public long getMobileRadioPowerStateUpdateRateLimit() {
+ return MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS;
+ }
+
public MockBatteryStatsImpl setNetworkStats(NetworkStats networkStats) {
mNetworkStats = networkStats;
return this;
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java
index a24afe6bc25e..808c1ec2603b 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java
@@ -31,8 +31,6 @@ import android.app.time.TimeCapabilities;
import android.app.time.TimeCapabilitiesAndConfig;
import android.app.time.TimeConfiguration;
-import androidx.test.runner.AndroidJUnit4;
-
import com.android.server.timedetector.TimeDetectorStrategy.Origin;
import org.junit.Test;
@@ -40,7 +38,14 @@ import org.junit.runner.RunWith;
import java.time.Instant;
-@RunWith(AndroidJUnit4.class)
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+/**
+ * Tests for {@link ConfigurationInternal} and associated {@link TimeCapabilitiesAndConfig}
+ * behavior.
+ */
+@RunWith(JUnitParamsRunner.class)
public class ConfigurationInternalTest {
private static final int ARBITRARY_USER_ID = 99999;
@@ -51,14 +56,15 @@ public class ConfigurationInternalTest {
private static final @Origin int[] ARBITRARY_ORIGIN_PRIORITIES = { ORIGIN_NETWORK };
/**
- * Tests when {@link ConfigurationInternal#isUserConfigAllowed()} and
- * {@link ConfigurationInternal#isAutoDetectionSupported()} are both true.
+ * Tests {@link TimeCapabilitiesAndConfig} behavior in different scenarios when auto detection
+ * is supported.
*/
@Test
- public void test_unrestricted() {
- ConfigurationInternal
- baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
- .setUserConfigAllowed(true)
+ @Parameters({ "true,true", "true,false", "false,true", "false,false" })
+ public void test_autoDetectionSupported_capabilitiesAndConfiguration(
+ boolean userConfigAllowed, boolean bypassUserPolicyChecks) {
+ ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+ .setUserConfigAllowed(userConfigAllowed)
.setAutoDetectionSupported(true)
.setSystemClockUpdateThresholdMillis(ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS)
.setAutoSuggestionLowerBound(ARBITRARY_SUGGESTION_LOWER_BOUND)
@@ -67,58 +73,8 @@ public class ConfigurationInternalTest {
.setOriginPriorities(ARBITRARY_ORIGIN_PRIORITIES)
.setAutoDetectionEnabledSetting(true)
.build();
- {
- ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
- .setAutoDetectionEnabledSetting(true)
- .build();
- assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
- assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior());
-
- TimeCapabilitiesAndConfig capabilitiesAndConfig = autoOnConfig.capabilitiesAndConfig();
-
- TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
- assertEquals(CAPABILITY_POSSESSED,
- capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSetManualTimeCapability());
-
- TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration();
- assertTrue(configuration.isAutoDetectionEnabled());
- }
- {
- ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
- .setAutoDetectionEnabledSetting(false)
- .build();
- assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
- assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
-
- TimeCapabilitiesAndConfig capabilitiesAndConfig = autoOffConfig.capabilitiesAndConfig();
-
- TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
- assertEquals(CAPABILITY_POSSESSED,
- capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_POSSESSED,
- capabilities.getSetManualTimeCapability());
-
- TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration();
- assertFalse(configuration.isAutoDetectionEnabled());
- }
- }
-
- /** Tests when {@link ConfigurationInternal#isUserConfigAllowed()} is false */
- @Test
- public void test_restricted() {
- ConfigurationInternal
- baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
- .setUserConfigAllowed(false)
- .setAutoDetectionSupported(true)
- .setSystemClockUpdateThresholdMillis(ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS)
- .setAutoSuggestionLowerBound(ARBITRARY_SUGGESTION_LOWER_BOUND)
- .setManualSuggestionLowerBound(ARBITRARY_SUGGESTION_LOWER_BOUND)
- .setSuggestionUpperBound(ARBITRARY_SUGGESTION_UPPER_BOUND)
- .setOriginPriorities(ARBITRARY_ORIGIN_PRIORITIES)
- .setAutoDetectionEnabledSetting(true)
- .build();
+ boolean userRestrictionsExpected = !(userConfigAllowed || bypassUserPolicyChecks);
{
ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
.setAutoDetectionEnabledSetting(true)
@@ -126,12 +82,19 @@ public class ConfigurationInternalTest {
assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior());
- TimeCapabilitiesAndConfig capabilitiesAndConfig = autoOnConfig.capabilitiesAndConfig();
+ TimeCapabilitiesAndConfig capabilitiesAndConfig =
+ autoOnConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
- assertEquals(CAPABILITY_NOT_ALLOWED,
- capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability());
+ if (userRestrictionsExpected) {
+ assertEquals(CAPABILITY_NOT_ALLOWED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability());
+ } else {
+ assertEquals(CAPABILITY_POSSESSED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSetManualTimeCapability());
+ }
TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration();
assertTrue(configuration.isAutoDetectionEnabled());
@@ -144,23 +107,35 @@ public class ConfigurationInternalTest {
assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
- TimeCapabilitiesAndConfig capabilitiesAndConfig = autoOffConfig.capabilitiesAndConfig();
+ TimeCapabilitiesAndConfig capabilitiesAndConfig =
+ autoOffConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
- assertEquals(CAPABILITY_NOT_ALLOWED,
- capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability());
-
+ if (userRestrictionsExpected) {
+ assertEquals(CAPABILITY_NOT_ALLOWED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability());
+ } else {
+ assertEquals(CAPABILITY_POSSESSED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_POSSESSED,
+ capabilities.getSetManualTimeCapability());
+ }
TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration();
assertFalse(configuration.isAutoDetectionEnabled());
}
}
- /** Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is false. */
+ /**
+ * Tests {@link TimeCapabilitiesAndConfig} behavior in different scenarios when auto detection
+ * is not supported.
+ */
@Test
- public void test_autoDetectNotSupported() {
+ @Parameters({ "true,true", "true,false", "false,true", "false,false" })
+ public void test_autoDetectNotSupported_capabilitiesAndConfiguration(
+ boolean userConfigAllowed, boolean bypassUserPolicyChecks) {
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
- .setUserConfigAllowed(true)
+ .setUserConfigAllowed(userConfigAllowed)
.setAutoDetectionSupported(false)
.setSystemClockUpdateThresholdMillis(ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS)
.setAutoSuggestionLowerBound(ARBITRARY_SUGGESTION_LOWER_BOUND)
@@ -169,6 +144,9 @@ public class ConfigurationInternalTest {
.setOriginPriorities(ARBITRARY_ORIGIN_PRIORITIES)
.setAutoDetectionEnabledSetting(true)
.build();
+ boolean userRestrictionsExpected = !(userConfigAllowed || bypassUserPolicyChecks);
+
+ // Auto-detection enabled.
{
ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
.setAutoDetectionEnabledSetting(true)
@@ -176,30 +154,41 @@ public class ConfigurationInternalTest {
assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
assertFalse(autoOnConfig.getAutoDetectionEnabledBehavior());
- TimeCapabilitiesAndConfig capabilitiesAndConfig = autoOnConfig.capabilitiesAndConfig();
+ TimeCapabilitiesAndConfig capabilitiesAndConfig =
+ autoOnConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
assertEquals(CAPABILITY_NOT_SUPPORTED,
capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeCapability());
+ if (userRestrictionsExpected) {
+ assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability());
+ } else {
+ assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeCapability());
+ }
TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration();
assertTrue(configuration.isAutoDetectionEnabled());
}
+
+ // Auto-detection disabled.
{
- ConfigurationInternal
- autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
+ ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
.setAutoDetectionEnabledSetting(false)
.build();
assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
- TimeCapabilitiesAndConfig capabilitiesAndConfig = autoOffConfig.capabilitiesAndConfig();
+ TimeCapabilitiesAndConfig capabilitiesAndConfig =
+ autoOffConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
assertEquals(CAPABILITY_NOT_SUPPORTED,
capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeCapability());
+ if (userRestrictionsExpected) {
+ assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability());
+ } else {
+ assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeCapability());
+ }
TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration();
assertFalse(configuration.isAutoDetectionEnabled());
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java b/services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java
index 77b319b56acb..a98a43b75d9b 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java
@@ -31,7 +31,7 @@ import java.util.ArrayList;
import java.util.List;
/** A partially implemented, fake implementation of ServiceConfigAccessor for tests. */
-class FakeServiceConfigAccessor implements ServiceConfigAccessor {
+public class FakeServiceConfigAccessor implements ServiceConfigAccessor {
private final List<ConfigurationChangeListener> mConfigurationInternalChangeListeners =
new ArrayList<>();
@@ -54,7 +54,8 @@ class FakeServiceConfigAccessor implements ServiceConfigAccessor {
@Override
public boolean updateConfiguration(
- @UserIdInt int userID, @NonNull TimeConfiguration requestedChanges) {
+ @UserIdInt int userID, @NonNull TimeConfiguration requestedChanges,
+ boolean bypassUserPolicyChecks) {
assertNotNull(mConfigurationInternal);
assertNotNull(requestedChanges);
@@ -62,7 +63,7 @@ class FakeServiceConfigAccessor implements ServiceConfigAccessor {
// old configuration merged with the new if the user has the capability to up the settings.
// Then, if the configuration changed, the change listener is invoked.
TimeCapabilitiesAndConfig capabilitiesAndConfig =
- mConfigurationInternal.capabilitiesAndConfig();
+ mConfigurationInternal.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration();
TimeConfiguration newConfiguration =
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java
index 7b38fa014512..856df359b326 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java
@@ -16,9 +16,6 @@
package com.android.server.timedetector;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
import android.annotation.UserIdInt;
import android.app.time.ExternalTimeSuggestion;
import android.app.time.TimeState;
@@ -31,20 +28,10 @@ import android.util.IndentingPrintWriter;
* A fake implementation of {@link com.android.server.timedetector.TimeDetectorStrategy} for use
* in tests.
*/
-class FakeTimeDetectorStrategy implements TimeDetectorStrategy {
+public class FakeTimeDetectorStrategy implements TimeDetectorStrategy {
// State
private TimeState mTimeState;
- // Call tracking.
- private TelephonyTimeSuggestion mLastTelephonySuggestion;
- private @UserIdInt Integer mLastManualSuggestionUserId;
- private ManualTimeSuggestion mLastManualSuggestion;
- private NetworkTimeSuggestion mLastNetworkSuggestion;
- private GnssTimeSuggestion mLastGnssSuggestion;
- private ExternalTimeSuggestion mLastExternalSuggestion;
- private UnixEpochTime mLastConfirmedTime;
- private boolean mDumpCalled;
-
@Override
public TimeState getTimeState() {
return mTimeState;
@@ -57,80 +44,32 @@ class FakeTimeDetectorStrategy implements TimeDetectorStrategy {
@Override
public boolean confirmTime(UnixEpochTime confirmationTime) {
- mLastConfirmedTime = confirmationTime;
return false;
}
@Override
public void suggestTelephonyTime(TelephonyTimeSuggestion timeSuggestion) {
- mLastTelephonySuggestion = timeSuggestion;
}
@Override
- public boolean suggestManualTime(@UserIdInt int userId, ManualTimeSuggestion timeSuggestion) {
- mLastManualSuggestionUserId = userId;
- mLastManualSuggestion = timeSuggestion;
+ public boolean suggestManualTime(@UserIdInt int userId, ManualTimeSuggestion timeSuggestion,
+ boolean bypassUserPolicyChecks) {
return true;
}
@Override
public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
- mLastNetworkSuggestion = timeSuggestion;
}
@Override
public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) {
- mLastGnssSuggestion = timeSuggestion;
}
@Override
public void suggestExternalTime(ExternalTimeSuggestion timeSuggestion) {
- mLastExternalSuggestion = timeSuggestion;
}
@Override
public void dump(IndentingPrintWriter pw, String[] args) {
- mDumpCalled = true;
- }
-
- void resetCallTracking() {
- mLastTelephonySuggestion = null;
- mLastManualSuggestionUserId = null;
- mLastManualSuggestion = null;
- mLastNetworkSuggestion = null;
- mLastGnssSuggestion = null;
- mLastExternalSuggestion = null;
- mLastConfirmedTime = null;
- mDumpCalled = false;
- }
-
- void verifySuggestTelephonyTimeCalled(TelephonyTimeSuggestion expectedSuggestion) {
- assertEquals(expectedSuggestion, mLastTelephonySuggestion);
- }
-
- void verifySuggestManualTimeCalled(
- @UserIdInt int expectedUserId, ManualTimeSuggestion expectedSuggestion) {
- assertEquals((Integer) expectedUserId, mLastManualSuggestionUserId);
- assertEquals(expectedSuggestion, mLastManualSuggestion);
- }
-
- void verifySuggestNetworkTimeCalled(NetworkTimeSuggestion expectedSuggestion) {
- assertEquals(expectedSuggestion, mLastNetworkSuggestion);
- }
-
- void verifySuggestGnssTimeCalled(GnssTimeSuggestion expectedSuggestion) {
- assertEquals(expectedSuggestion, mLastGnssSuggestion);
- }
-
- void verifySuggestExternalTimeCalled(ExternalTimeSuggestion expectedSuggestion) {
- assertEquals(expectedSuggestion, mLastExternalSuggestion);
- }
-
- void verifyConfirmTimeCalled(UnixEpochTime expectedConfirmationTime) {
- assertEquals(mLastConfirmedTime, expectedConfirmationTime);
- }
-
- void verifyDumpCalled() {
- assertTrue(mDumpCalled);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java
index 5b6175217568..a0845a64757e 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java
@@ -16,14 +16,24 @@
package com.android.server.timedetector;
+import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import android.app.time.TimeCapabilitiesAndConfig;
+import android.app.time.TimeConfiguration;
import android.app.time.UnixEpochTime;
+import android.app.timedetector.ManualTimeSuggestion;
import android.content.Context;
import android.os.HandlerThread;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.timezonedetector.TestCurrentUserIdentityInjector;
import com.android.server.timezonedetector.TestHandler;
import org.junit.After;
@@ -31,15 +41,25 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.time.Instant;
+
@RunWith(AndroidJUnit4.class)
public class TimeDetectorInternalImplTest {
+ private static final int ARBITRARY_USER_ID = 9999;
+ private static final int ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS = 1234;
+ private static final Instant ARBITRARY_SUGGESTION_LOWER_BOUND = Instant.ofEpochMilli(0);
+ private static final Instant ARBITRARY_SUGGESTION_UPPER_BOUND =
+ Instant.ofEpochMilli(Long.MAX_VALUE);
+ private static final int[] ARBITRARY_ORIGIN_PRIORITIES = { ORIGIN_NETWORK };
private Context mMockContext;
- private FakeTimeDetectorStrategy mFakeTimeDetectorStrategy;
-
- private TimeDetectorInternalImpl mTimeDetectorInternal;
private HandlerThread mHandlerThread;
private TestHandler mTestHandler;
+ private TestCurrentUserIdentityInjector mTestCurrentUserIdentityInjector;
+ private FakeServiceConfigAccessor mFakeServiceConfigAccessorSpy;
+ private FakeTimeDetectorStrategy mFakeTimeDetectorStrategySpy;
+
+ private TimeDetectorInternalImpl mTimeDetectorInternal;
@Before
public void setUp() {
@@ -49,11 +69,14 @@ public class TimeDetectorInternalImplTest {
mHandlerThread = new HandlerThread("TimeDetectorInternalTest");
mHandlerThread.start();
mTestHandler = new TestHandler(mHandlerThread.getLooper());
-
- mFakeTimeDetectorStrategy = new FakeTimeDetectorStrategy();
+ mTestCurrentUserIdentityInjector = new TestCurrentUserIdentityInjector();
+ mTestCurrentUserIdentityInjector.initializeCurrentUserId(ARBITRARY_USER_ID);
+ mFakeServiceConfigAccessorSpy = spy(new FakeServiceConfigAccessor());
+ mFakeTimeDetectorStrategySpy = spy(new FakeTimeDetectorStrategy());
mTimeDetectorInternal = new TimeDetectorInternalImpl(
- mMockContext, mTestHandler, mFakeTimeDetectorStrategy);
+ mMockContext, mTestHandler, mTestCurrentUserIdentityInjector,
+ mFakeServiceConfigAccessorSpy, mFakeTimeDetectorStrategySpy);
}
@After
@@ -63,6 +86,56 @@ public class TimeDetectorInternalImplTest {
}
@Test
+ public void testGetCapabilitiesAndConfigForDpm() throws Exception {
+ final boolean autoDetectionEnabled = true;
+ ConfigurationInternal testConfig = createConfigurationInternal(autoDetectionEnabled);
+ mFakeServiceConfigAccessorSpy.initializeConfiguration(testConfig);
+
+ TimeCapabilitiesAndConfig actualCapabilitiesAndConfig =
+ mTimeDetectorInternal.getCapabilitiesAndConfigForDpm();
+
+ int expectedUserId = mTestCurrentUserIdentityInjector.getCurrentUserId();
+ verify(mFakeServiceConfigAccessorSpy).getConfigurationInternal(expectedUserId);
+
+ final boolean bypassUserPolicyChecks = true;
+ TimeCapabilitiesAndConfig expectedCapabilitiesAndConfig =
+ testConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
+ assertEquals(expectedCapabilitiesAndConfig, actualCapabilitiesAndConfig);
+ }
+
+ @Test
+ public void testUpdateConfigurationForDpm() throws Exception {
+ final boolean autoDetectionEnabled = false;
+ ConfigurationInternal initialConfigurationInternal =
+ createConfigurationInternal(autoDetectionEnabled);
+ mFakeServiceConfigAccessorSpy.initializeConfiguration(initialConfigurationInternal);
+
+ TimeConfiguration timeConfiguration = new TimeConfiguration.Builder()
+ .setAutoDetectionEnabled(true)
+ .build();
+ assertTrue(mTimeDetectorInternal.updateConfigurationForDpm(timeConfiguration));
+
+ final boolean expectedBypassUserPolicyChecks = true;
+ verify(mFakeServiceConfigAccessorSpy).updateConfiguration(
+ mTestCurrentUserIdentityInjector.getCurrentUserId(),
+ timeConfiguration,
+ expectedBypassUserPolicyChecks);
+ }
+
+ @Test
+ public void testSetManualTimeZoneForDpm() throws Exception {
+ ManualTimeSuggestion timeSuggestion = createManualTimeSuggestion();
+
+ // The fake strategy always returns true.
+ assertTrue(mTimeDetectorInternal.setManualTimeForDpm(timeSuggestion));
+
+ int expectedUserId = mTestCurrentUserIdentityInjector.getCurrentUserId();
+ boolean expectedBypassUserPolicyChecks = false;
+ verify(mFakeTimeDetectorStrategySpy).suggestManualTime(
+ expectedUserId, timeSuggestion, expectedBypassUserPolicyChecks);
+ }
+
+ @Test
public void testSuggestNetworkTime() throws Exception {
NetworkTimeSuggestion networkTimeSuggestion = createNetworkTimeSuggestion();
@@ -70,7 +143,7 @@ public class TimeDetectorInternalImplTest {
mTestHandler.assertTotalMessagesEnqueued(1);
mTestHandler.waitForMessagesToBeProcessed();
- mFakeTimeDetectorStrategy.verifySuggestNetworkTimeCalled(networkTimeSuggestion);
+ verify(mFakeTimeDetectorStrategySpy).suggestNetworkTime(networkTimeSuggestion);
}
private static NetworkTimeSuggestion createNetworkTimeSuggestion() {
@@ -86,11 +159,29 @@ public class TimeDetectorInternalImplTest {
mTestHandler.assertTotalMessagesEnqueued(1);
mTestHandler.waitForMessagesToBeProcessed();
- mFakeTimeDetectorStrategy.verifySuggestGnssTimeCalled(gnssTimeSuggestion);
+ verify(mFakeTimeDetectorStrategySpy).suggestGnssTime(gnssTimeSuggestion);
+ }
+
+ private static ManualTimeSuggestion createManualTimeSuggestion() {
+ UnixEpochTime timeValue = new UnixEpochTime(100L, 1_000_000L);
+ return new ManualTimeSuggestion(timeValue);
}
private static GnssTimeSuggestion createGnssTimeSuggestion() {
UnixEpochTime timeValue = new UnixEpochTime(100L, 1_000_000L);
return new GnssTimeSuggestion(timeValue);
}
+
+ private static ConfigurationInternal createConfigurationInternal(boolean autoDetectionEnabled) {
+ return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+ .setUserConfigAllowed(true)
+ .setAutoDetectionSupported(true)
+ .setSystemClockUpdateThresholdMillis(ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS)
+ .setAutoSuggestionLowerBound(ARBITRARY_SUGGESTION_LOWER_BOUND)
+ .setManualSuggestionLowerBound(ARBITRARY_SUGGESTION_LOWER_BOUND)
+ .setSuggestionUpperBound(ARBITRARY_SUGGESTION_UPPER_BOUND)
+ .setOriginPriorities(ARBITRARY_ORIGIN_PRIORITIES)
+ .setAutoDetectionEnabledSetting(autoDetectionEnabled)
+ .build();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index f776d3da0e79..4b417ba367ae 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -31,12 +31,14 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.time.ExternalTimeSuggestion;
import android.app.time.ITimeDetectorListener;
+import android.app.time.TimeCapabilitiesAndConfig;
import android.app.time.TimeConfiguration;
import android.app.time.TimeState;
import android.app.time.UnixEpochTime;
@@ -77,14 +79,14 @@ public class TimeDetectorServiceTest {
private Context mMockContext;
- private TimeDetectorService mTimeDetectorService;
private HandlerThread mHandlerThread;
private TestHandler mTestHandler;
private TestCallerIdentityInjector mTestCallerIdentityInjector;
- private FakeServiceConfigAccessor mFakeServiceConfigAccessor;
- private NtpTrustedTime mMockNtpTrustedTime;
- private FakeTimeDetectorStrategy mFakeTimeDetectorStrategy;
+ private FakeServiceConfigAccessor mFakeServiceConfigAccessorSpy;
+ private FakeTimeDetectorStrategy mFakeTimeDetectorStrategySpy;
+ private NtpTrustedTime mMockNtpTrustedTime;
+ private TimeDetectorService mTimeDetectorService;
@Before
public void setUp() {
@@ -98,13 +100,13 @@ public class TimeDetectorServiceTest {
mTestCallerIdentityInjector = new TestCallerIdentityInjector();
mTestCallerIdentityInjector.initializeCallingUserId(ARBITRARY_USER_ID);
- mFakeTimeDetectorStrategy = new FakeTimeDetectorStrategy();
- mFakeServiceConfigAccessor = new FakeServiceConfigAccessor();
+ mFakeServiceConfigAccessorSpy = spy(new FakeServiceConfigAccessor());
+ mFakeTimeDetectorStrategySpy = spy(new FakeTimeDetectorStrategy());
mMockNtpTrustedTime = mock(NtpTrustedTime.class);
mTimeDetectorService = new TimeDetectorService(
- mMockContext, mTestHandler, mTestCallerIdentityInjector, mFakeServiceConfigAccessor,
- mFakeTimeDetectorStrategy, mMockNtpTrustedTime);
+ mMockContext, mTestHandler, mTestCallerIdentityInjector,
+ mFakeServiceConfigAccessorSpy, mFakeTimeDetectorStrategySpy, mMockNtpTrustedTime);
}
@After
@@ -129,13 +131,19 @@ public class TimeDetectorServiceTest {
ConfigurationInternal configuration =
createConfigurationInternal(true /* autoDetectionEnabled*/);
- mFakeServiceConfigAccessor.initializeConfiguration(configuration);
-
- assertEquals(configuration.capabilitiesAndConfig(),
- mTimeDetectorService.getCapabilitiesAndConfig());
+ mFakeServiceConfigAccessorSpy.initializeConfiguration(configuration);
+ TimeCapabilitiesAndConfig actualCapabilitiesAndConfig =
+ mTimeDetectorService.getCapabilitiesAndConfig();
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
+ int expectedUserId = mTestCallerIdentityInjector.getCallingUserId();
+ verify(mFakeServiceConfigAccessorSpy).getConfigurationInternal(expectedUserId);
+
+ boolean bypassUserPolicyChecks = false;
+ TimeCapabilitiesAndConfig expectedCapabilitiesAndConfig =
+ configuration.createCapabilitiesAndConfig(bypassUserPolicyChecks);
+ assertEquals(expectedCapabilitiesAndConfig, actualCapabilitiesAndConfig);
}
@Test
@@ -165,7 +173,7 @@ public class TimeDetectorServiceTest {
public void testListenerRegistrationAndCallbacks() throws Exception {
ConfigurationInternal initialConfiguration =
createConfigurationInternal(false /* autoDetectionEnabled */);
- mFakeServiceConfigAccessor.initializeConfiguration(initialConfiguration);
+ mFakeServiceConfigAccessorSpy.initializeConfiguration(initialConfiguration);
IBinder mockListenerBinder = mock(IBinder.class);
ITimeDetectorListener mockListener = mock(ITimeDetectorListener.class);
@@ -258,32 +266,34 @@ public class TimeDetectorServiceTest {
eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString());
mTestHandler.waitForMessagesToBeProcessed();
- mFakeTimeDetectorStrategy.verifySuggestTelephonyTimeCalled(timeSuggestion);
+ verify(mFakeTimeDetectorStrategySpy).suggestTelephonyTime(timeSuggestion);
}
@Test
public void testSuggestManualTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
- .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ .when(mMockContext).enforceCallingPermission(anyString(), any());
ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion();
assertThrows(SecurityException.class,
() -> mTimeDetectorService.suggestManualTime(manualTimeSuggestion));
- verify(mMockContext).enforceCallingOrSelfPermission(
+ verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString());
}
@Test
public void testSuggestManualTime() throws Exception {
- doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion();
assertTrue(mTimeDetectorService.suggestManualTime(manualTimeSuggestion));
- mFakeTimeDetectorStrategy.verifySuggestManualTimeCalled(
- mTestCallerIdentityInjector.getCallingUserId(), manualTimeSuggestion);
+ int expectedUserId = mTestCallerIdentityInjector.getCallingUserId();
+ boolean expectedBypassUserPolicyChecks = false;
+ verify(mFakeTimeDetectorStrategySpy).suggestManualTime(
+ expectedUserId, manualTimeSuggestion, expectedBypassUserPolicyChecks);
- verify(mMockContext).enforceCallingOrSelfPermission(
+ verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString());
}
@@ -291,55 +301,55 @@ public class TimeDetectorServiceTest {
@Test
public void testSuggestNetworkTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
- .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ .when(mMockContext).enforceCallingPermission(anyString(), any());
NetworkTimeSuggestion networkTimeSuggestion = createNetworkTimeSuggestion();
assertThrows(SecurityException.class,
() -> mTimeDetectorService.suggestNetworkTime(networkTimeSuggestion));
- verify(mMockContext).enforceCallingOrSelfPermission(
+ verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SET_TIME), anyString());
}
@Test
public void testSuggestNetworkTime() throws Exception {
- doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
NetworkTimeSuggestion networkTimeSuggestion = createNetworkTimeSuggestion();
mTimeDetectorService.suggestNetworkTime(networkTimeSuggestion);
mTestHandler.assertTotalMessagesEnqueued(1);
- verify(mMockContext).enforceCallingOrSelfPermission(
+ verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SET_TIME), anyString());
mTestHandler.waitForMessagesToBeProcessed();
- mFakeTimeDetectorStrategy.verifySuggestNetworkTimeCalled(networkTimeSuggestion);
+ verify(mFakeTimeDetectorStrategySpy).suggestNetworkTime(networkTimeSuggestion);
}
@Test
public void testSuggestGnssTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
- .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ .when(mMockContext).enforceCallingPermission(anyString(), any());
GnssTimeSuggestion gnssTimeSuggestion = createGnssTimeSuggestion();
assertThrows(SecurityException.class,
() -> mTimeDetectorService.suggestGnssTime(gnssTimeSuggestion));
- verify(mMockContext).enforceCallingOrSelfPermission(
+ verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SET_TIME), anyString());
}
@Test
public void testSuggestGnssTime() throws Exception {
- doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
GnssTimeSuggestion gnssTimeSuggestion = createGnssTimeSuggestion();
mTimeDetectorService.suggestGnssTime(gnssTimeSuggestion);
mTestHandler.assertTotalMessagesEnqueued(1);
- verify(mMockContext).enforceCallingOrSelfPermission(
+ verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SET_TIME), anyString());
mTestHandler.waitForMessagesToBeProcessed();
- mFakeTimeDetectorStrategy.verifySuggestGnssTimeCalled(gnssTimeSuggestion);
+ verify(mFakeTimeDetectorStrategySpy).suggestGnssTime(gnssTimeSuggestion);
}
@Test
@@ -366,7 +376,7 @@ public class TimeDetectorServiceTest {
eq(android.Manifest.permission.SUGGEST_EXTERNAL_TIME), anyString());
mTestHandler.waitForMessagesToBeProcessed();
- mFakeTimeDetectorStrategy.verifySuggestExternalTimeCalled(externalTimeSuggestion);
+ verify(mFakeTimeDetectorStrategySpy).suggestExternalTime(externalTimeSuggestion);
}
@Test
@@ -390,7 +400,7 @@ public class TimeDetectorServiceTest {
public void testGetTimeState() {
doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
TimeState fakeState = new TimeState(new UnixEpochTime(12345L, 98765L), true);
- mFakeTimeDetectorStrategy.setTimeState(fakeState);
+ mFakeTimeDetectorStrategySpy.setTimeState(fakeState);
TimeState actualState = mTimeDetectorService.getTimeState();
@@ -418,7 +428,7 @@ public class TimeDetectorServiceTest {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
- assertEquals(mFakeTimeDetectorStrategy.getTimeState(), state);
+ assertEquals(mFakeTimeDetectorStrategySpy.getTimeState(), state);
}
@Test
@@ -442,7 +452,7 @@ public class TimeDetectorServiceTest {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
- mFakeTimeDetectorStrategy.verifyConfirmTimeCalled(confirmationTime);
+ verify(mFakeTimeDetectorStrategySpy).confirmTime(confirmationTime);
}
@Test
@@ -467,8 +477,10 @@ public class TimeDetectorServiceTest {
mTimeDetectorService.setManualTime(timeSuggestion));
// The service calls "suggestManualTime()" because the logic is the same.
- mFakeTimeDetectorStrategy.verifySuggestManualTimeCalled(
- mTestCallerIdentityInjector.getCallingUserId(), timeSuggestion);
+ int expectedUserId = mTestCallerIdentityInjector.getCallingUserId();
+ boolean expectedBypassUserPolicyChecks = false;
+ verify(mFakeTimeDetectorStrategySpy).suggestManualTime(
+ expectedUserId, timeSuggestion, expectedBypassUserPolicyChecks);
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
@@ -495,7 +507,7 @@ public class TimeDetectorServiceTest {
mTimeDetectorService.dump(null, pw, null);
verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
- mFakeTimeDetectorStrategy.verifyDumpCalled();
+ verify(mFakeTimeDetectorStrategySpy).dump(any(), any());
}
private static TimeConfiguration createTimeConfiguration(boolean autoDetectionEnabled) {
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index 08632284cfc8..62dae481ac0b 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -38,8 +38,6 @@ import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.os.TimestampedValue;
-import androidx.test.runner.AndroidJUnit4;
-
import com.android.server.SystemClockTime.TimeConfidence;
import com.android.server.timedetector.TimeDetectorStrategy.Origin;
import com.android.server.timezonedetector.ConfigurationChangeListener;
@@ -55,7 +53,10 @@ import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Objects;
-@RunWith(AndroidJUnit4.class)
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+@RunWith(JUnitParamsRunner.class)
public class TimeDetectorStrategyImplTest {
private static final @UserIdInt int ARBITRARY_USER_ID = 9876;
@@ -656,12 +657,45 @@ public class TimeDetectorStrategyImplTest {
long expectedSystemClockMillis =
script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime());
+ boolean bypassUserPolicyChecks = false;
+ boolean expectedResult = true;
script.simulateManualTimeSuggestion(
- ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */)
+ ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult)
.verifySystemClockConfidence(TIME_CONFIDENCE_HIGH)
.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis);
}
+ /** Confirms the effect of user policy restrictions on being able to set the time. */
+ @Test
+ @Parameters({ "true,true", "true,false", "false,true", "false,false" })
+ public void testSuggestManualTime_autoTimeDisabled_userRestrictions(
+ boolean userConfigAllowed, boolean bypassUserPolicyChecks) {
+ ConfigurationInternal configAutoDisabled =
+ new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED)
+ .setUserConfigAllowed(userConfigAllowed)
+ .build();
+ Script script = new Script().simulateConfigurationInternalChange(configAutoDisabled)
+ .verifySystemClockConfidence(TIME_CONFIDENCE_LOW);
+
+ ManualTimeSuggestion timeSuggestion =
+ script.generateManualTimeSuggestion(ARBITRARY_TEST_TIME);
+
+ script.simulateTimePassing();
+
+ long expectedSystemClockMillis =
+ script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime());
+ boolean expectedResult = userConfigAllowed || bypassUserPolicyChecks;
+ script.simulateManualTimeSuggestion(
+ ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult);
+ if (expectedResult) {
+ script.verifySystemClockConfidence(TIME_CONFIDENCE_HIGH)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis);
+ } else {
+ script.verifySystemClockConfidence(TIME_CONFIDENCE_LOW)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+ }
+ }
+
@Test
public void testSuggestManualTime_retainsAutoSignal() {
Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED)
@@ -704,8 +738,10 @@ public class TimeDetectorStrategyImplTest {
long expectedManualClockMillis =
script.calculateTimeInMillisForNow(manualTimeSuggestion.getUnixEpochTime());
+ boolean bypassUserPolicyChecks = false;
+ boolean expectedResult = true;
script.simulateManualTimeSuggestion(
- ARBITRARY_USER_ID, manualTimeSuggestion, true /* expectedResult */)
+ ARBITRARY_USER_ID, manualTimeSuggestion, bypassUserPolicyChecks, expectedResult)
.verifySystemClockConfidence(TIME_CONFIDENCE_HIGH)
.verifySystemClockWasSetAndResetCallTracking(expectedManualClockMillis)
.assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion);
@@ -737,9 +773,10 @@ public class TimeDetectorStrategyImplTest {
ManualTimeSuggestion timeSuggestion =
script.generateManualTimeSuggestion(ARBITRARY_TEST_TIME);
- script.simulateTimePassing()
- .simulateManualTimeSuggestion(
- ARBITRARY_USER_ID, timeSuggestion, false /* expectedResult */)
+ boolean bypassUserPolicyChecks = false;
+ boolean expectedResult = false;
+ script.simulateTimePassing().simulateManualTimeSuggestion(
+ ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult)
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW)
.verifySystemClockWasNotSetAndResetCallTracking();
}
@@ -755,8 +792,10 @@ public class TimeDetectorStrategyImplTest {
Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1);
ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(aboveUpperBound);
+ boolean bypassUserPolicyChecks = false;
+ boolean expectedResult = false;
script.simulateManualTimeSuggestion(
- ARBITRARY_USER_ID, timeSuggestion, false /* expectedResult */)
+ ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult)
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW)
.verifySystemClockWasNotSetAndResetCallTracking();
}
@@ -772,8 +811,10 @@ public class TimeDetectorStrategyImplTest {
Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1);
ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(belowUpperBound);
+ boolean bypassUserPolicyChecks = false;
+ boolean expectedResult = true;
script.simulateManualTimeSuggestion(
- ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */)
+ ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult)
.verifySystemClockConfidence(TIME_CONFIDENCE_HIGH)
.verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli());
}
@@ -789,8 +830,10 @@ public class TimeDetectorStrategyImplTest {
Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1);
ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(belowLowerBound);
+ boolean bypassUserPolicyChecks = false;
+ boolean expectedResult = false;
script.simulateManualTimeSuggestion(
- ARBITRARY_USER_ID, timeSuggestion, false /* expectedResult */)
+ ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult)
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW)
.verifySystemClockWasNotSetAndResetCallTracking();
}
@@ -806,8 +849,10 @@ public class TimeDetectorStrategyImplTest {
Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1);
ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(aboveLowerBound);
+ boolean bypassUserPolicyChecks = false;
+ boolean expectedResult = true;
script.simulateManualTimeSuggestion(
- ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */)
+ ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult)
.verifySystemClockConfidence(TIME_CONFIDENCE_HIGH)
.verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli());
}
@@ -1758,8 +1803,10 @@ public class TimeDetectorStrategyImplTest {
ManualTimeSuggestion timeSuggestion =
script.generateManualTimeSuggestion(ARBITRARY_TEST_TIME);
+ boolean bypassUserPolicyChecks = false;
+ boolean expectedResult = true;
script.simulateManualTimeSuggestion(
- ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */)
+ ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult)
.verifySystemClockWasSetAndResetCallTracking(ARBITRARY_TEST_TIME.toEpochMilli());
}
@@ -1951,14 +1998,15 @@ public class TimeDetectorStrategyImplTest {
Script simulateManualTimeSuggestion(
@UserIdInt int userId, ManualTimeSuggestion timeSuggestion,
- boolean expectedResult) {
+ boolean bypassUserPolicyChecks, boolean expectedResult) {
String errorMessage = expectedResult
? "Manual time suggestion was ignored, but expected to be accepted."
: "Manual time suggestion was accepted, but expected to be ignored.";
assertEquals(
errorMessage,
expectedResult,
- mTimeDetectorStrategy.suggestManualTime(userId, timeSuggestion));
+ mTimeDetectorStrategy.suggestManualTime(
+ userId, timeSuggestion, bypassUserPolicyChecks));
return this;
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 810bd82f4431..7140097bb6c0 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -29,27 +29,36 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.app.time.TimeCapabilitiesAndConfig;
import android.app.time.TimeZoneCapabilities;
import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
/**
- * Tests for {@link ConfigurationInternal} and the {@link TimeZoneCapabilitiesAndConfig}.
+ * Tests for {@link ConfigurationInternal} and associated {@link TimeZoneCapabilitiesAndConfig}
+ * behavior.
*/
+@RunWith(JUnitParamsRunner.class)
public class ConfigurationInternalTest {
private static final int ARBITRARY_USER_ID = 99999;
/**
- * Tests when {@link ConfigurationInternal#isUserConfigAllowed()} and
- * {@link ConfigurationInternal#isAutoDetectionSupported()} are both true.
+ * Tests {@link TimeCapabilitiesAndConfig} behavior in different scenarios when auto detection
+ * is supported (and geo detection is supported)
*/
@Test
- public void test_unrestricted() {
+ @Parameters({ "true,true", "true,false", "false,true", "false,false" })
+ public void test_autoDetectionSupported_capabilitiesAndConfiguration(
+ boolean userConfigAllowed, boolean bypassUserPolicyChecks) {
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
- .setUserConfigAllowed(true)
+ .setUserConfigAllowed(userConfigAllowed)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setGeoDetectionRunInBackgroundEnabled(false)
@@ -59,73 +68,10 @@ public class ConfigurationInternalTest {
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
.build();
- {
- ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
- .setAutoDetectionEnabledSetting(true)
- .build();
- assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
- assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
- assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior());
- assertTrue(autoOnConfig.isGeoDetectionExecutionEnabled());
- assertEquals(DETECTION_MODE_GEO, autoOnConfig.getDetectionMode());
-
- TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
- autoOnConfig.createCapabilitiesAndConfig();
- TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
- assertEquals(CAPABILITY_POSSESSED,
- capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_NOT_APPLICABLE,
- capabilities.getSetManualTimeZoneCapability());
- assertEquals(CAPABILITY_POSSESSED,
- capabilities.getConfigureGeoDetectionEnabledCapability());
-
- TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration();
- assertTrue(configuration.isAutoDetectionEnabled());
- assertTrue(configuration.isGeoDetectionEnabled());
- }
+ boolean userRestrictionsExpected = !(userConfigAllowed || bypassUserPolicyChecks);
- {
- ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
- .setAutoDetectionEnabledSetting(false)
- .build();
- assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
- assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
- assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
- assertFalse(autoOffConfig.isGeoDetectionExecutionEnabled());
- assertEquals(DETECTION_MODE_MANUAL, autoOffConfig.getDetectionMode());
-
- TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
- autoOffConfig.createCapabilitiesAndConfig();
-
- TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
- assertEquals(CAPABILITY_POSSESSED,
- capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_POSSESSED,
- capabilities.getSetManualTimeZoneCapability());
- assertEquals(CAPABILITY_NOT_APPLICABLE,
- capabilities.getConfigureGeoDetectionEnabledCapability());
-
- TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration();
- assertFalse(configuration.isAutoDetectionEnabled());
- assertTrue(configuration.isGeoDetectionEnabled());
- }
- }
-
- /** Tests when {@link ConfigurationInternal#isUserConfigAllowed()} is false */
- @Test
- public void test_restricted() {
- ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
- .setUserConfigAllowed(false)
- .setTelephonyDetectionFeatureSupported(true)
- .setGeoDetectionFeatureSupported(true)
- .setTelephonyFallbackSupported(false)
- .setGeoDetectionRunInBackgroundEnabled(false)
- .setEnhancedMetricsCollectionEnabled(false)
- .setAutoDetectionEnabledSetting(true)
- .setLocationEnabledSetting(true)
- .setGeoDetectionEnabledSetting(true)
- .build();
+ // Auto-detection enabled.
{
ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
.setAutoDetectionEnabledSetting(true)
@@ -137,13 +83,20 @@ public class ConfigurationInternalTest {
assertEquals(DETECTION_MODE_GEO, autoOnConfig.getDetectionMode());
TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
- autoOnConfig.createCapabilitiesAndConfig();
+ autoOnConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
- assertEquals(CAPABILITY_NOT_ALLOWED,
- capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_NOT_ALLOWED,
- capabilities.getSetManualTimeZoneCapability());
+ if (userRestrictionsExpected) {
+ assertEquals(CAPABILITY_NOT_ALLOWED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_NOT_ALLOWED,
+ capabilities.getSetManualTimeZoneCapability());
+ } else {
+ assertEquals(CAPABILITY_POSSESSED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_NOT_APPLICABLE,
+ capabilities.getSetManualTimeZoneCapability());
+ }
// This has user privacy implications so it is not restricted in the same way as others.
assertEquals(CAPABILITY_POSSESSED,
capabilities.getConfigureGeoDetectionEnabledCapability());
@@ -153,6 +106,7 @@ public class ConfigurationInternalTest {
assertTrue(configuration.isGeoDetectionEnabled());
}
+ // Auto-detection disabled.
{
ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
.setAutoDetectionEnabledSetting(false)
@@ -164,13 +118,20 @@ public class ConfigurationInternalTest {
assertEquals(DETECTION_MODE_MANUAL, autoOffConfig.getDetectionMode());
TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
- autoOffConfig.createCapabilitiesAndConfig();
+ autoOffConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
- assertEquals(CAPABILITY_NOT_ALLOWED,
- capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_NOT_ALLOWED,
- capabilities.getSetManualTimeZoneCapability());
+ if (userRestrictionsExpected) {
+ assertEquals(CAPABILITY_NOT_ALLOWED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_NOT_ALLOWED,
+ capabilities.getSetManualTimeZoneCapability());
+ } else {
+ assertEquals(CAPABILITY_POSSESSED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_POSSESSED,
+ capabilities.getSetManualTimeZoneCapability());
+ }
// This has user privacy implications so it is not restricted in the same way as others.
assertEquals(CAPABILITY_NOT_APPLICABLE,
capabilities.getConfigureGeoDetectionEnabledCapability());
@@ -181,11 +142,16 @@ public class ConfigurationInternalTest {
}
}
- /** Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is false. */
+ /**
+ * Tests {@link TimeCapabilitiesAndConfig} behavior in different scenarios when auto detection
+ * is not supported.
+ */
@Test
- public void test_autoDetectNotSupported() {
+ @Parameters({ "true,true", "true,false", "false,true", "false,false" })
+ public void test_autoDetectNotSupported_capabilitiesAndConfiguration(
+ boolean userConfigAllowed, boolean bypassUserPolicyChecks) {
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
- .setUserConfigAllowed(true)
+ .setUserConfigAllowed(userConfigAllowed)
.setTelephonyDetectionFeatureSupported(false)
.setGeoDetectionFeatureSupported(false)
.setGeoDetectionRunInBackgroundEnabled(false)
@@ -195,6 +161,10 @@ public class ConfigurationInternalTest {
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
.build();
+
+ boolean userRestrictionsExpected = !(userConfigAllowed || bypassUserPolicyChecks);
+
+ // Auto-detection enabled.
{
ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
.setAutoDetectionEnabledSetting(true)
@@ -206,12 +176,16 @@ public class ConfigurationInternalTest {
assertEquals(DETECTION_MODE_MANUAL, autoOnConfig.getDetectionMode());
TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
- autoOnConfig.createCapabilitiesAndConfig();
+ autoOnConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
assertEquals(CAPABILITY_NOT_SUPPORTED,
capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeZoneCapability());
+ if (userRestrictionsExpected) {
+ assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeZoneCapability());
+ } else {
+ assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeZoneCapability());
+ }
assertEquals(CAPABILITY_NOT_SUPPORTED,
capabilities.getConfigureGeoDetectionEnabledCapability());
@@ -219,6 +193,8 @@ public class ConfigurationInternalTest {
assertTrue(configuration.isAutoDetectionEnabled());
assertTrue(configuration.isGeoDetectionEnabled());
}
+
+ // Auto-detection disabled.
{
ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
.setAutoDetectionEnabledSetting(false)
@@ -230,12 +206,16 @@ public class ConfigurationInternalTest {
assertEquals(DETECTION_MODE_MANUAL, autoOffConfig.getDetectionMode());
TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
- autoOffConfig.createCapabilitiesAndConfig();
+ autoOffConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
assertEquals(CAPABILITY_NOT_SUPPORTED,
capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeZoneCapability());
+ if (userRestrictionsExpected) {
+ assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeZoneCapability());
+ } else {
+ assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeZoneCapability());
+ }
assertEquals(CAPABILITY_NOT_SUPPORTED,
capabilities.getConfigureGeoDetectionEnabledCapability());
@@ -246,13 +226,15 @@ public class ConfigurationInternalTest {
}
/**
- * Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is true, but
- * {@link ConfigurationInternal#isGeoDetectionSupported()} is false.
+ * Tests {@link TimeCapabilitiesAndConfig} behavior in different scenarios when auto detection
+ * is supported (and geo detection is not supported).
*/
@Test
- public void test_geoDetectNotSupported() {
+ @Parameters({ "true,true", "true,false", "false,true", "false,false" })
+ public void test_geoDetectNotSupported_capabilitiesAndConfiguration(
+ boolean userConfigAllowed, boolean bypassUserPolicyChecks) {
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
- .setUserConfigAllowed(true)
+ .setUserConfigAllowed(userConfigAllowed)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(false)
.setGeoDetectionRunInBackgroundEnabled(false)
@@ -262,6 +244,10 @@ public class ConfigurationInternalTest {
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
.build();
+
+ boolean userRestrictionsExpected = !(userConfigAllowed || bypassUserPolicyChecks);
+
+ // Auto-detection enabled.
{
ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
.setAutoDetectionEnabledSetting(true)
@@ -273,13 +259,20 @@ public class ConfigurationInternalTest {
assertEquals(DETECTION_MODE_TELEPHONY, autoOnConfig.getDetectionMode());
TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
- autoOnConfig.createCapabilitiesAndConfig();
+ autoOnConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
- assertEquals(CAPABILITY_POSSESSED,
- capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_NOT_APPLICABLE,
- capabilities.getSetManualTimeZoneCapability());
+ if (userRestrictionsExpected) {
+ assertEquals(CAPABILITY_NOT_ALLOWED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_NOT_ALLOWED,
+ capabilities.getSetManualTimeZoneCapability());
+ } else {
+ assertEquals(CAPABILITY_POSSESSED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_NOT_APPLICABLE,
+ capabilities.getSetManualTimeZoneCapability());
+ }
assertEquals(CAPABILITY_NOT_SUPPORTED,
capabilities.getConfigureGeoDetectionEnabledCapability());
@@ -287,6 +280,8 @@ public class ConfigurationInternalTest {
assertTrue(configuration.isAutoDetectionEnabled());
assertTrue(configuration.isGeoDetectionEnabled());
}
+
+ // Auto-detection disabled.
{
ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
.setAutoDetectionEnabledSetting(false)
@@ -298,12 +293,18 @@ public class ConfigurationInternalTest {
assertEquals(DETECTION_MODE_MANUAL, autoOffConfig.getDetectionMode());
TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
- autoOffConfig.createCapabilitiesAndConfig();
+ autoOffConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
- assertEquals(CAPABILITY_POSSESSED,
- capabilities.getConfigureAutoDetectionEnabledCapability());
- assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeZoneCapability());
+ if (userRestrictionsExpected) {
+ assertEquals(CAPABILITY_NOT_ALLOWED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeZoneCapability());
+ } else {
+ assertEquals(CAPABILITY_POSSESSED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeZoneCapability());
+ }
assertEquals(CAPABILITY_NOT_SUPPORTED,
capabilities.getConfigureGeoDetectionEnabledCapability());
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
index a97ad8c57a0d..fdee86ea1285 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
@@ -32,7 +32,7 @@ import java.util.List;
import java.util.Optional;
/** A partially implemented, fake implementation of ServiceConfigAccessor for tests. */
-class FakeServiceConfigAccessor implements ServiceConfigAccessor {
+public class FakeServiceConfigAccessor implements ServiceConfigAccessor {
private final List<ConfigurationChangeListener> mConfigurationInternalChangeListeners =
new ArrayList<>();
@@ -55,7 +55,8 @@ class FakeServiceConfigAccessor implements ServiceConfigAccessor {
@Override
public boolean updateConfiguration(
- @UserIdInt int userID, @NonNull TimeZoneConfiguration requestedChanges) {
+ @UserIdInt int userID, @NonNull TimeZoneConfiguration requestedChanges,
+ boolean bypassUserPolicyChecks) {
assertNotNull(mConfigurationInternal);
assertNotNull(requestedChanges);
@@ -63,7 +64,7 @@ class FakeServiceConfigAccessor implements ServiceConfigAccessor {
// old configuration merged with the new if the user has the capability to up the settings.
// Then, if the configuration changed, the change listener is invoked.
TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
- mConfigurationInternal.createCapabilitiesAndConfig();
+ mConfigurationInternal.createCapabilitiesAndConfig(bypassUserPolicyChecks);
TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration();
TimeZoneConfiguration newConfiguration =
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index 339e5b23cd9a..228dc952356a 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -15,9 +15,6 @@
*/
package com.android.server.timezonedetector;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.time.TimeZoneState;
@@ -25,21 +22,12 @@ import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.util.IndentingPrintWriter;
-class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
+public class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
private TimeZoneState mTimeZoneState;
- // Call tracking.
- private GeolocationTimeZoneSuggestion mLastGeolocationSuggestion;
- private ManualTimeZoneSuggestion mLastManualSuggestion;
- private Integer mLastManualSuggestionUserId;
- private TelephonyTimeZoneSuggestion mLastTelephonySuggestion;
- private String mLastConfirmedTimeZone;
- private boolean mDumpCalled;
-
@Override
public boolean confirmTimeZone(String timeZoneId) {
- mLastConfirmedTimeZone = timeZoneId;
return false;
}
@@ -55,21 +43,17 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
@Override
public void suggestGeolocationTimeZone(GeolocationTimeZoneSuggestion timeZoneSuggestion) {
- mLastGeolocationSuggestion = timeZoneSuggestion;
}
@Override
public boolean suggestManualTimeZone(
- @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
- mLastManualSuggestionUserId = userId;
- mLastManualSuggestion = timeZoneSuggestion;
+ @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion timeZoneSuggestion,
+ boolean bypassUserPolicyChecks) {
return true;
}
@Override
- public void suggestTelephonyTimeZone(
- @NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) {
- mLastTelephonySuggestion = timeZoneSuggestion;
+ public void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) {
}
@Override
@@ -94,38 +78,5 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
@Override
public void dump(IndentingPrintWriter pw, String[] args) {
- mDumpCalled = true;
- }
-
- void resetCallTracking() {
- mLastGeolocationSuggestion = null;
- mLastManualSuggestion = null;
- mLastManualSuggestionUserId = null;
- mLastTelephonySuggestion = null;
- mDumpCalled = false;
- mLastConfirmedTimeZone = null;
- }
-
- void verifySuggestGeolocationTimeZoneCalled(
- GeolocationTimeZoneSuggestion expectedSuggestion) {
- assertEquals(expectedSuggestion, mLastGeolocationSuggestion);
- }
-
- void verifySuggestManualTimeZoneCalled(
- @UserIdInt int expectedUserId, ManualTimeZoneSuggestion expectedSuggestion) {
- assertEquals((Integer) expectedUserId, mLastManualSuggestionUserId);
- assertEquals(expectedSuggestion, mLastManualSuggestion);
- }
-
- void verifySuggestTelephonyTimeZoneCalled(TelephonyTimeZoneSuggestion expectedSuggestion) {
- assertEquals(expectedSuggestion, mLastTelephonySuggestion);
- }
-
- void verifyDumpCalled() {
- assertTrue(mDumpCalled);
- }
-
- void verifyConfirmTimeZoneCalled(String expectedTimeZoneId) {
- assertEquals(expectedTimeZoneId, mLastConfirmedTimeZone);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java
new file mode 100644
index 000000000000..aad06d87f111
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2022 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.timezonedetector;
+
+import android.annotation.UserIdInt;
+
+/** A fake {@link CurrentUserIdentityInjector} used in tests. */
+public class TestCurrentUserIdentityInjector implements CurrentUserIdentityInjector {
+
+ private Integer mCurrentUserId;
+
+ public void initializeCurrentUserId(@UserIdInt int userId) {
+ mCurrentUserId = userId;
+ }
+
+ @Override
+ public int getCurrentUserId() {
+ return mCurrentUserId;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
index c5bab760fcfe..276fdb971172 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
@@ -16,8 +16,15 @@
package com.android.server.timezonedetector;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import android.app.time.TimeZoneCapabilitiesAndConfig;
+import android.app.time.TimeZoneConfiguration;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.content.Context;
import android.os.HandlerThread;
@@ -35,15 +42,18 @@ import java.util.List;
public class TimeZoneDetectorInternalImplTest {
private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234L;
- private static final List<String> ARBITRARY_ZONE_IDS = Arrays.asList("TestZoneId");
+ private static final String ARBITRARY_ZONE_ID = "TestZoneId";
+ private static final List<String> ARBITRARY_ZONE_IDS = Arrays.asList(ARBITRARY_ZONE_ID);
+ private static final int ARBITRARY_USER_ID = 9999;
private Context mMockContext;
- private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy;
-
- private TimeZoneDetectorInternalImpl mTimeZoneDetectorInternal;
private HandlerThread mHandlerThread;
private TestHandler mTestHandler;
+ private TestCurrentUserIdentityInjector mTestCurrentUserIdentityInjector;
+ private FakeServiceConfigAccessor mFakeServiceConfigAccessorSpy;
+ private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategySpy;
+ private TimeZoneDetectorInternalImpl mTimeZoneDetectorInternal;
@Before
public void setUp() {
@@ -53,11 +63,14 @@ public class TimeZoneDetectorInternalImplTest {
mHandlerThread = new HandlerThread("TimeZoneDetectorInternalTest");
mHandlerThread.start();
mTestHandler = new TestHandler(mHandlerThread.getLooper());
-
- mFakeTimeZoneDetectorStrategy = new FakeTimeZoneDetectorStrategy();
+ mTestCurrentUserIdentityInjector = new TestCurrentUserIdentityInjector();
+ mTestCurrentUserIdentityInjector.initializeCurrentUserId(ARBITRARY_USER_ID);
+ mFakeServiceConfigAccessorSpy = spy(new FakeServiceConfigAccessor());
+ mFakeTimeZoneDetectorStrategySpy = spy(new FakeTimeZoneDetectorStrategy());
mTimeZoneDetectorInternal = new TimeZoneDetectorInternalImpl(
- mMockContext, mTestHandler, mFakeTimeZoneDetectorStrategy);
+ mMockContext, mTestHandler, mTestCurrentUserIdentityInjector,
+ mFakeServiceConfigAccessorSpy, mFakeTimeZoneDetectorStrategySpy);
}
@After
@@ -67,17 +80,84 @@ public class TimeZoneDetectorInternalImplTest {
}
@Test
+ public void testGetCapabilitiesAndConfigForDpm() throws Exception {
+ final boolean autoDetectionEnabled = true;
+ ConfigurationInternal testConfig = createConfigurationInternal(autoDetectionEnabled);
+ mFakeServiceConfigAccessorSpy.initializeConfiguration(testConfig);
+
+ TimeZoneCapabilitiesAndConfig actualCapabilitiesAndConfig =
+ mTimeZoneDetectorInternal.getCapabilitiesAndConfigForDpm();
+
+ int expectedUserId = mTestCurrentUserIdentityInjector.getCurrentUserId();
+ verify(mFakeServiceConfigAccessorSpy).getConfigurationInternal(expectedUserId);
+
+ final boolean bypassUserPolicyChecks = true;
+ TimeZoneCapabilitiesAndConfig expectedCapabilitiesAndConfig =
+ testConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks);
+ assertEquals(expectedCapabilitiesAndConfig, actualCapabilitiesAndConfig);
+ }
+
+ @Test
+ public void testUpdateConfigurationForDpm() throws Exception {
+ final boolean autoDetectionEnabled = false;
+ ConfigurationInternal initialConfigurationInternal =
+ createConfigurationInternal(autoDetectionEnabled);
+ mFakeServiceConfigAccessorSpy.initializeConfiguration(initialConfigurationInternal);
+
+ TimeZoneConfiguration timeConfiguration = new TimeZoneConfiguration.Builder()
+ .setAutoDetectionEnabled(true)
+ .build();
+ assertTrue(mTimeZoneDetectorInternal.updateConfigurationForDpm(timeConfiguration));
+
+ final boolean expectedBypassUserPolicyChecks = true;
+ verify(mFakeServiceConfigAccessorSpy).updateConfiguration(
+ mTestCurrentUserIdentityInjector.getCurrentUserId(),
+ timeConfiguration,
+ expectedBypassUserPolicyChecks);
+ }
+
+ @Test
+ public void testSetManualTimeZoneForDpm() throws Exception {
+ ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion();
+
+ // The fake strategy always returns true.
+ assertTrue(mTimeZoneDetectorInternal.setManualTimeZoneForDpm(timeZoneSuggestion));
+
+ int expectedUserId = mTestCurrentUserIdentityInjector.getCurrentUserId();
+ boolean expectedBypassUserPolicyChecks = true;
+ verify(mFakeTimeZoneDetectorStrategySpy).suggestManualTimeZone(
+ expectedUserId, timeZoneSuggestion, expectedBypassUserPolicyChecks);
+ }
+
+ @Test
public void testSuggestGeolocationTimeZone() throws Exception {
GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion();
mTimeZoneDetectorInternal.suggestGeolocationTimeZone(timeZoneSuggestion);
mTestHandler.assertTotalMessagesEnqueued(1);
mTestHandler.waitForMessagesToBeProcessed();
- mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion);
+ verify(mFakeTimeZoneDetectorStrategySpy).suggestGeolocationTimeZone(timeZoneSuggestion);
+ }
+ private static ManualTimeZoneSuggestion createManualTimeZoneSuggestion() {
+ return new ManualTimeZoneSuggestion(ARBITRARY_ZONE_ID);
}
private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() {
return GeolocationTimeZoneSuggestion.createCertainSuggestion(
ARBITRARY_ELAPSED_REALTIME_MILLIS, ARBITRARY_ZONE_IDS);
}
+
+ private static ConfigurationInternal createConfigurationInternal(boolean autoDetectionEnabled) {
+ return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+ .setTelephonyDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(true)
+ .setTelephonyFallbackSupported(false)
+ .setGeoDetectionRunInBackgroundEnabled(false)
+ .setEnhancedMetricsCollectionEnabled(false)
+ .setUserConfigAllowed(true)
+ .setAutoDetectionEnabledSetting(autoDetectionEnabled)
+ .setLocationEnabledSetting(false)
+ .setGeoDetectionEnabledSetting(false)
+ .build();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index fc6459725978..bb9d564b5aad 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -28,11 +28,13 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.time.ITimeZoneDetectorListener;
+import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
import android.app.time.TimeZoneState;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
@@ -67,8 +69,8 @@ public class TimeZoneDetectorServiceTest {
private HandlerThread mHandlerThread;
private TestHandler mTestHandler;
private TestCallerIdentityInjector mTestCallerIdentityInjector;
- private FakeServiceConfigAccessor mFakeServiceConfigAccessor;
- private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy;
+ private FakeServiceConfigAccessor mFakeServiceConfigAccessorSpy;
+ private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategySpy;
@Before
@@ -83,12 +85,12 @@ public class TimeZoneDetectorServiceTest {
mTestCallerIdentityInjector = new TestCallerIdentityInjector();
mTestCallerIdentityInjector.initializeCallingUserId(ARBITRARY_USER_ID);
- mFakeTimeZoneDetectorStrategy = new FakeTimeZoneDetectorStrategy();
- mFakeServiceConfigAccessor = new FakeServiceConfigAccessor();
+ mFakeServiceConfigAccessorSpy = spy(new FakeServiceConfigAccessor());
+ mFakeTimeZoneDetectorStrategySpy = spy(new FakeTimeZoneDetectorStrategy());
mTimeZoneDetectorService = new TimeZoneDetectorService(
mMockContext, mTestHandler, mTestCallerIdentityInjector,
- mFakeServiceConfigAccessor, mFakeTimeZoneDetectorStrategy);
+ mFakeServiceConfigAccessorSpy, mFakeTimeZoneDetectorStrategySpy);
}
@After
@@ -113,13 +115,21 @@ public class TimeZoneDetectorServiceTest {
ConfigurationInternal configuration =
createConfigurationInternal(true /* autoDetectionEnabled*/);
- mFakeServiceConfigAccessor.initializeConfiguration(configuration);
+ mFakeServiceConfigAccessorSpy.initializeConfiguration(configuration);
- assertEquals(configuration.createCapabilitiesAndConfig(),
- mTimeZoneDetectorService.getCapabilitiesAndConfig());
+ TimeZoneCapabilitiesAndConfig actualCapabilitiesAndConfig =
+ mTimeZoneDetectorService.getCapabilitiesAndConfig();
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
+
+ int expectedUserId = mTestCallerIdentityInjector.getCallingUserId();
+ verify(mFakeServiceConfigAccessorSpy).getConfigurationInternal(expectedUserId);
+
+ boolean expectedBypassUserPolicyChecks = false;
+ TimeZoneCapabilitiesAndConfig expectedCapabilitiesAndConfig =
+ configuration.createCapabilitiesAndConfig(expectedBypassUserPolicyChecks);
+ assertEquals(expectedCapabilitiesAndConfig, actualCapabilitiesAndConfig);
}
@Test
@@ -150,7 +160,7 @@ public class TimeZoneDetectorServiceTest {
public void testListenerRegistrationAndCallbacks() throws Exception {
ConfigurationInternal initialConfiguration =
createConfigurationInternal(false /* autoDetectionEnabled */);
- mFakeServiceConfigAccessor.initializeConfiguration(initialConfiguration);
+ mFakeServiceConfigAccessorSpy.initializeConfiguration(initialConfiguration);
IBinder mockListenerBinder = mock(IBinder.class);
ITimeZoneDetectorListener mockListener = mock(ITimeZoneDetectorListener.class);
@@ -222,46 +232,46 @@ public class TimeZoneDetectorServiceTest {
@Test
public void testSuggestGeolocationTimeZone_withoutPermission() {
doThrow(new SecurityException("Mock"))
- .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ .when(mMockContext).enforceCallingPermission(anyString(), any());
GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion();
assertThrows(SecurityException.class,
() -> mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion));
- verify(mMockContext).enforceCallingOrSelfPermission(
+ verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SET_TIME_ZONE), anyString());
}
@Test
public void testSuggestGeolocationTimeZone() throws Exception {
- doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion();
mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion);
mTestHandler.assertTotalMessagesEnqueued(1);
- verify(mMockContext).enforceCallingOrSelfPermission(
+ verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SET_TIME_ZONE), anyString());
mTestHandler.waitForMessagesToBeProcessed();
- mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion);
+ verify(mFakeTimeZoneDetectorStrategySpy).suggestGeolocationTimeZone(timeZoneSuggestion);
}
@Test
public void testSuggestManualTimeZone_withoutPermission() {
doThrow(new SecurityException("Mock"))
- .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ .when(mMockContext).enforceCallingPermission(anyString(), any());
ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion();
assertThrows(SecurityException.class,
() -> mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion));
- verify(mMockContext).enforceCallingOrSelfPermission(
+ verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString());
}
@Test
public void testSuggestManualTimeZone() throws Exception {
- doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion();
@@ -269,10 +279,12 @@ public class TimeZoneDetectorServiceTest {
assertEquals(expectedResult,
mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion));
- mFakeTimeZoneDetectorStrategy.verifySuggestManualTimeZoneCalled(
- mTestCallerIdentityInjector.getCallingUserId(), timeZoneSuggestion);
+ int expectedUserId = mTestCallerIdentityInjector.getCallingUserId();
+ boolean expectedBypassUserPolicyChecks = false;
+ verify(mFakeTimeZoneDetectorStrategySpy).suggestManualTimeZone(
+ expectedUserId, timeZoneSuggestion, expectedBypassUserPolicyChecks);
- verify(mMockContext).enforceCallingOrSelfPermission(
+ verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString());
}
@@ -312,20 +324,20 @@ public class TimeZoneDetectorServiceTest {
eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString());
mTestHandler.waitForMessagesToBeProcessed();
- mFakeTimeZoneDetectorStrategy.verifySuggestTelephonyTimeZoneCalled(timeZoneSuggestion);
+ verify(mFakeTimeZoneDetectorStrategySpy).suggestTelephonyTimeZone(timeZoneSuggestion);
}
@Test
public void testGetTimeZoneState() {
doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
TimeZoneState fakeState = new TimeZoneState("Europe/Narnia", true);
- mFakeTimeZoneDetectorStrategy.setTimeZoneState(fakeState);
+ mFakeTimeZoneDetectorStrategySpy.setTimeZoneState(fakeState);
TimeZoneState actualState = mTimeZoneDetectorService.getTimeZoneState();
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
- assertEquals(actualState, fakeState);
+ assertEquals(fakeState, actualState);
}
@Test
@@ -347,7 +359,7 @@ public class TimeZoneDetectorServiceTest {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
- assertEquals(mFakeTimeZoneDetectorStrategy.getTimeZoneState(), state);
+ assertEquals(state, mFakeTimeZoneDetectorStrategySpy.getTimeZoneState());
}
@Test
@@ -371,7 +383,7 @@ public class TimeZoneDetectorServiceTest {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
- mFakeTimeZoneDetectorStrategy.verifyConfirmTimeZoneCalled("Europe/Narnia");
+ verify(mFakeTimeZoneDetectorStrategySpy).confirmTimeZone("Europe/Narnia");
}
@Test
@@ -396,8 +408,10 @@ public class TimeZoneDetectorServiceTest {
mTimeZoneDetectorService.setManualTimeZone(timeZoneSuggestion));
// The service calls "suggestManualTimeZone()" because the logic is the same.
- mFakeTimeZoneDetectorStrategy.verifySuggestManualTimeZoneCalled(
- mTestCallerIdentityInjector.getCallingUserId(), timeZoneSuggestion);
+ int expectedUserId = mTestCallerIdentityInjector.getCallingUserId();
+ boolean expectedBypassUserPolicyChecks = false;
+ verify(mFakeTimeZoneDetectorStrategySpy).suggestManualTimeZone(
+ expectedUserId, timeZoneSuggestion, expectedBypassUserPolicyChecks);
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
@@ -427,7 +441,7 @@ public class TimeZoneDetectorServiceTest {
mTimeZoneDetectorService.dump(null, pw, null);
verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
- mFakeTimeZoneDetectorStrategy.verifyDumpCalled();
+ verify(mFakeTimeZoneDetectorStrategySpy).dump(any(), any());
verify(dumpable).dump(any(), any());
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 77d8b8bf9687..d0a7c92b0436 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -53,6 +53,7 @@ import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.Qualifie
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -60,9 +61,13 @@ import java.util.Collections;
import java.util.List;
import java.util.function.Function;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
/**
* White-box unit tests for {@link TimeZoneDetectorStrategyImpl}.
*/
+@RunWith(JUnitParamsRunner.class)
public class TimeZoneDetectorStrategyImplTest {
private static final @UserIdInt int USER_ID = 9876;
@@ -561,74 +566,79 @@ public class TimeZoneDetectorStrategyImplTest {
.resetConfigurationTracking();
// Auto time zone detection is enabled so the manual suggestion should be ignored.
- script.simulateManualTimeZoneSuggestion(
- USER_ID, createManualSuggestion("Europe/Paris"), false /* expectedResult */)
+ boolean bypassUserPolicyChecks = false;
+ boolean expectedResult = false;
+ script.simulateManualTimeZoneSuggestion(USER_ID, createManualSuggestion("Europe/Paris"),
+ bypassUserPolicyChecks, expectedResult)
.verifyTimeZoneNotChanged();
assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion());
}
@Test
- public void testManualSuggestion_restricted_simulateAutoTimeZoneEnabled() {
- Script script = new Script()
- .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
- .simulateConfigurationInternalChange(CONFIG_USER_RESTRICTED_AUTO_ENABLED)
- .resetConfigurationTracking();
-
- // User is restricted so the manual suggestion should be ignored.
- script.simulateManualTimeZoneSuggestion(
- USER_ID, createManualSuggestion("Europe/Paris"), false /* expectedResult */)
- .verifyTimeZoneNotChanged();
-
- assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion());
- }
-
- @Test
- public void testManualSuggestion_unrestricted_autoTimeZoneDetectionDisabled() {
+ public void testManualSuggestion_autoDetectNotSupported() {
Script script = new Script()
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
- .simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED_GEO_DISABLED)
+ .simulateConfigurationInternalChange(CONFIG_AUTO_DETECT_NOT_SUPPORTED)
.resetConfigurationTracking();
- // Auto time zone detection is disabled so the manual suggestion should be used.
+ // Unrestricted users have the capability.
ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
+ boolean bypassUserPolicyChecks = false;
+ boolean expectedResult = true;
script.simulateManualTimeZoneSuggestion(
- USER_ID, manualSuggestion, true /* expectedResult */)
- .verifyTimeZoneChangedAndReset(manualSuggestion);
+ USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult)
+ .verifyTimeZoneChangedAndReset(manualSuggestion);
assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion());
}
@Test
- public void testManualSuggestion_restricted_autoTimeZoneDetectionDisabled() {
+ @Parameters({ "true,true", "true,false", "false,true", "false,false" })
+ public void testManualSuggestion_autoTimeEnabled_userRestrictions(
+ boolean userConfigAllowed, boolean bypassUserPolicyChecks) {
+ ConfigurationInternal config =
+ new ConfigurationInternal.Builder(CONFIG_USER_RESTRICTED_AUTO_ENABLED)
+ .setUserConfigAllowed(userConfigAllowed)
+ .build();
Script script = new Script()
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
- .simulateConfigurationInternalChange(CONFIG_USER_RESTRICTED_AUTO_DISABLED)
+ .simulateConfigurationInternalChange(config)
.resetConfigurationTracking();
- // Restricted users do not have the capability.
- ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
- script.simulateManualTimeZoneSuggestion(
- USER_ID, manualSuggestion, false /* expectedResult */)
+ // User is restricted so the manual suggestion should be ignored.
+ boolean expectedResult = false;
+ script.simulateManualTimeZoneSuggestion(USER_ID, createManualSuggestion("Europe/Paris"),
+ bypassUserPolicyChecks, expectedResult)
.verifyTimeZoneNotChanged();
assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion());
}
@Test
- public void testManualSuggestion_autoDetectNotSupported() {
+ @Parameters({ "true,true", "true,false", "false,true", "false,false" })
+ public void testManualSuggestion_autoTimeDisabled_userRestrictions(
+ boolean userConfigAllowed, boolean bypassUserPolicyChecks) {
+ ConfigurationInternal config =
+ new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED_GEO_DISABLED)
+ .setUserConfigAllowed(userConfigAllowed)
+ .build();
Script script = new Script()
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
- .simulateConfigurationInternalChange(CONFIG_AUTO_DETECT_NOT_SUPPORTED)
+ .simulateConfigurationInternalChange(config)
.resetConfigurationTracking();
- // Unrestricted users have the capability.
ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
+ boolean expectedResult = userConfigAllowed || bypassUserPolicyChecks;
script.simulateManualTimeZoneSuggestion(
- USER_ID, manualSuggestion, true /* expectedResult */)
- .verifyTimeZoneChangedAndReset(manualSuggestion);
-
- assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion());
+ USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult);
+ if (expectedResult) {
+ script.verifyTimeZoneChangedAndReset(manualSuggestion);
+ assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion());
+ } else {
+ script.verifyTimeZoneNotChanged();
+ assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion());
+ }
}
@Test
@@ -1071,8 +1081,10 @@ public class TimeZoneDetectorStrategyImplTest {
// Make sure the manual suggestion is recorded.
ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Zone1");
- script.simulateManualTimeZoneSuggestion(USER_ID, manualSuggestion,
- true /* expectedResult */)
+ boolean bypassUserPolicyChecks = false;
+ boolean expectedResult = true;
+ script.simulateManualTimeZoneSuggestion(
+ USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult)
.verifyTimeZoneChangedAndReset(manualSuggestion);
expectedDeviceTimeZoneId = manualSuggestion.getZoneId();
assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
@@ -1350,9 +1362,9 @@ public class TimeZoneDetectorStrategyImplTest {
/** Simulates the time zone detection strategy receiving a user-originated suggestion. */
Script simulateManualTimeZoneSuggestion(
@UserIdInt int userId, ManualTimeZoneSuggestion manualTimeZoneSuggestion,
- boolean expectedResult) {
+ boolean bypassUserPolicyChecks, boolean expectedResult) {
boolean actualResult = mTimeZoneDetectorStrategy.suggestManualTimeZone(
- userId, manualTimeZoneSuggestion);
+ userId, manualTimeZoneSuggestion, bypassUserPolicyChecks);
assertEquals(expectedResult, actualResult);
return this;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 041f298d4aa2..f2bc47dfd6e9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -21,6 +21,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
+import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
@@ -45,6 +47,7 @@ import static org.mockito.Mockito.verify;
import android.app.StatusBarManager;
import android.platform.test.annotations.Presubmit;
+import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -70,7 +73,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
@Test
public void testControlsForDispatch_regular() {
- addWindow(TYPE_STATUS_BAR, "statusBar");
+ addStatusBar();
addWindow(TYPE_NAVIGATION_BAR, "navBar");
final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
@@ -82,7 +85,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
@Test
public void testControlsForDispatch_multiWindowTaskVisible() {
- addWindow(TYPE_STATUS_BAR, "statusBar");
+ addStatusBar();
addWindow(TYPE_NAVIGATION_BAR, "navBar");
final WindowState win = createWindow(null, WINDOWING_MODE_MULTI_WINDOW,
@@ -95,7 +98,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
@Test
public void testControlsForDispatch_freeformTaskVisible() {
- addWindow(TYPE_STATUS_BAR, "statusBar");
+ addStatusBar();
addWindow(TYPE_NAVIGATION_BAR, "navBar");
final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM,
@@ -108,8 +111,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
@Test
public void testControlsForDispatch_forceStatusBarVisible() {
- addWindow(TYPE_STATUS_BAR, "statusBar").mAttrs.privateFlags |=
- PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
+ addStatusBar().mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
addWindow(TYPE_NAVIGATION_BAR, "navBar");
final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
@@ -123,7 +125,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
public void testControlsForDispatch_statusBarForceShowNavigation() {
addWindow(TYPE_NOTIFICATION_SHADE, "notificationShade").mAttrs.privateFlags |=
PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
- addWindow(TYPE_STATUS_BAR, "statusBar");
+ addStatusBar();
addWindow(TYPE_NAVIGATION_BAR, "navBar");
final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
@@ -152,7 +154,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
public void testControlsForDispatch_remoteInsetsControllerControlsBars_appHasNoControl() {
mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(true);
- addWindow(TYPE_STATUS_BAR, "statusBar");
+ addStatusBar();
addWindow(TYPE_NAVIGATION_BAR, "navBar");
final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
@@ -163,7 +165,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
@Test
public void testControlsForDispatch_topAppHidesStatusBar() {
- addWindow(TYPE_STATUS_BAR, "statusBar");
+ addStatusBar();
addWindow(TYPE_NAVIGATION_BAR, "navBar");
// Add a fullscreen (MATCH_PARENT x MATCH_PARENT) app window which hides status bar.
@@ -256,7 +258,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
@SetupWindows(addWindows = W_ACTIVITY)
@Test
public void testShowTransientBars_bothCanBeTransient_appGetsBothFakeControls() {
- final WindowState statusBar = addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar");
+ final WindowState statusBar = addStatusBar();
statusBar.setHasSurface(true);
statusBar.getControllableInsetProvider().setServerVisible(true);
final WindowState navBar = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar");
@@ -298,8 +300,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
@SetupWindows(addWindows = W_ACTIVITY)
@Test
public void testShowTransientBars_statusBarCanBeTransient_appGetsStatusBarFakeControl() {
- addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
- .getControllableInsetProvider().getSource().setVisible(false);
+ addStatusBar().getControllableInsetProvider().getSource().setVisible(false);
addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
.getControllableInsetProvider().setServerVisible(true);
@@ -328,8 +329,8 @@ public class InsetsPolicyTest extends WindowTestsBase {
@SetupWindows(addWindows = W_ACTIVITY)
@Test
public void testAbortTransientBars_bothCanBeAborted_appGetsBothRealControls() {
- final InsetsSource statusBarSource = addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
- .getControllableInsetProvider().getSource();
+ final InsetsSource statusBarSource =
+ addStatusBar().getControllableInsetProvider().getSource();
final InsetsSource navBarSource = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
.getControllableInsetProvider().getSource();
statusBarSource.setVisible(false);
@@ -381,8 +382,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
@Test
public void testShowTransientBars_abortsWhenControlTargetChanges() {
- addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
- .getControllableInsetProvider().getSource().setVisible(false);
+ addStatusBar().getControllableInsetProvider().getSource().setVisible(false);
addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
.getControllableInsetProvider().getSource().setVisible(false);
final WindowState app = addWindow(TYPE_APPLICATION, "app");
@@ -406,6 +406,18 @@ public class InsetsPolicyTest extends WindowTestsBase {
return win;
}
+ private WindowState addStatusBar() {
+ final WindowState win = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ win.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+ win.mAttrs.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(ITYPE_STATUS_BAR),
+ new InsetsFrameProvider(ITYPE_TOP_TAPPABLE_ELEMENT),
+ new InsetsFrameProvider(ITYPE_TOP_MANDATORY_GESTURES)
+ };
+ mDisplayContent.getDisplayPolicy().addWindowLw(win, win.mAttrs);
+ return win;
+ }
+
private WindowState addWindow(int type, String name) {
final WindowState win = createWindow(null, type, name);
mDisplayContent.getDisplayPolicy().addWindowLw(win, win.mAttrs);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 54b33e9794b6..d59fce089292 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -26,6 +26,8 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
+import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
@@ -84,6 +86,7 @@ import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
+import android.view.InsetsFrameProvider;
import android.view.InsetsVisibilities;
import android.view.WindowManager;
@@ -3079,6 +3082,11 @@ public class SizeCompatTests extends WindowTestsBase {
attrs.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
attrs.setFitInsetsTypes(0 /* types */);
+ attrs.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(ITYPE_STATUS_BAR),
+ new InsetsFrameProvider(ITYPE_TOP_TAPPABLE_ELEMENT),
+ new InsetsFrameProvider(ITYPE_TOP_MANDATORY_GESTURES)
+ };
final TestWindowState statusBar = new TestWindowState(
displayContent.mWmService, mock(Session.class), new TestIWindow(), attrs, token);
token.addWindow(statusBar);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 70e6f2947c36..42bbd2d40f41 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -79,6 +79,7 @@ import com.android.server.LockGuard;
import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService;
+import com.android.server.display.DisplayControl;
import com.android.server.display.color.ColorDisplayService;
import com.android.server.firewall.IntentFirewall;
import com.android.server.input.InputManagerService;
@@ -177,6 +178,7 @@ public class SystemServicesTestRule implements TestRule {
.mockStatic(LocalServices.class, spyStubOnly)
.mockStatic(DeviceConfig.class, spyStubOnly)
.mockStatic(SurfaceControl.class, mockStubOnly)
+ .mockStatic(DisplayControl.class, mockStubOnly)
.mockStatic(LockGuard.class, mockStubOnly)
.mockStatic(Watchdog.class, mockStubOnly)
.strictness(Strictness.LENIENT)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 8370691f34f6..cf24ff2e99a5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -217,6 +217,12 @@ public class WindowManagerServiceTests extends WindowTestsBase {
mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.VISIBLE, 0, 0, 0,
outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
assertEquals(0, outConfig.getMergedConfiguration().densityDpi);
+ // Non activity window can still get the last config.
+ win.mActivityRecord = null;
+ win.fillClientWindowFramesAndConfiguration(outFrames, outConfig,
+ false /* useLatestConfig */, true /* relayoutVisible */);
+ assertEquals(win.getConfiguration().densityDpi,
+ outConfig.getMergedConfiguration().densityDpi);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index c597b46ff73f..40326e9ad7f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -27,6 +27,9 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.os.Process.SYSTEM_UID;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
+import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
@@ -324,6 +327,11 @@ class WindowTestsBase extends SystemServiceTestsBase {
mStatusBarWindow.mAttrs.layoutInDisplayCutoutMode =
LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mStatusBarWindow.mAttrs.setFitInsetsTypes(0);
+ mStatusBarWindow.mAttrs.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(ITYPE_STATUS_BAR),
+ new InsetsFrameProvider(ITYPE_TOP_TAPPABLE_ELEMENT),
+ new InsetsFrameProvider(ITYPE_TOP_MANDATORY_GESTURES)
+ };
}
if (addAll || ArrayUtils.contains(requestedWindows, W_NOTIFICATION_SHADE)) {
mNotificationShadeWindow = createCommonWindow(null, TYPE_NOTIFICATION_SHADE,
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index 1e74451a8d4d..2d382a27d747 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -17,6 +17,7 @@
package com.android.server.translation;
import static android.view.translation.TranslationManager.EXTRA_CAPABILITIES;
+import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL;
import static android.view.translation.UiTranslationManager.EXTRA_PACKAGE_NAME;
import static android.view.translation.UiTranslationManager.EXTRA_SOURCE_LOCALE;
import static android.view.translation.UiTranslationManager.EXTRA_STATE;
@@ -32,7 +33,9 @@ import android.annotation.SuppressLint;
import android.app.Activity;
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.Bundle;
import android.os.IBinder;
@@ -40,6 +43,7 @@ import android.os.IRemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.service.translation.TranslationService;
import android.service.translation.TranslationServiceInfo;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -156,12 +160,27 @@ final class TranslationManagerServiceImpl extends
return null;
}
final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+ if (!isServiceAvailableForUser(serviceComponent)) {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "ensureRemoteServiceLocked(): " + serviceComponent
+ + " is not available,");
+ }
+ return null;
+ }
mRemoteTranslationService = new RemoteTranslationService(getContext(), serviceComponent,
mUserId, /* isInstantAllowed= */ false, mRemoteServiceCallback);
}
return mRemoteTranslationService;
}
+ private boolean isServiceAvailableForUser(ComponentName serviceComponent) {
+ Intent intent = new Intent(TranslationService.SERVICE_INTERFACE)
+ .setComponent(serviceComponent);
+ final ResolveInfo resolveInfo = getContext().getPackageManager().resolveServiceAsUser(
+ intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, mUserId);
+ return resolveInfo != null && resolveInfo.serviceInfo != null;
+ }
+
@GuardedBy("mLock")
void onTranslationCapabilitiesRequestLocked(@TranslationSpec.DataFormat int sourceFormat,
@TranslationSpec.DataFormat int destFormat,
@@ -170,6 +189,9 @@ final class TranslationManagerServiceImpl extends
if (remoteService != null) {
remoteService.onTranslationCapabilitiesRequest(sourceFormat, destFormat,
resultReceiver);
+ } else {
+ Slog.v(TAG, "onTranslationCapabilitiesRequestLocked(): no remote service.");
+ resultReceiver.send(STATUS_SYNC_CALL_FAIL, null);
}
}
@@ -184,10 +206,13 @@ final class TranslationManagerServiceImpl extends
@GuardedBy("mLock")
void onSessionCreatedLocked(@NonNull TranslationContext translationContext, int sessionId,
- IResultReceiver resultReceiver) {
+ IResultReceiver resultReceiver) throws RemoteException {
final RemoteTranslationService remoteService = ensureRemoteServiceLocked();
if (remoteService != null) {
remoteService.onSessionCreated(translationContext, sessionId, resultReceiver);
+ } else {
+ Slog.v(TAG, "onSessionCreatedLocked(): no remote service.");
+ resultReceiver.send(STATUS_SYNC_CALL_FAIL, null);
}
}
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index de70dcb9b29c..79ab009d3b92 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -27,6 +27,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.SystemProperties;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import java.io.PrintWriter;
@@ -192,4 +193,59 @@ public final class TelephonyUtils {
// This is the error case. The well-defined value for UNKNOWN is -1.
return "UNKNOWN(" + state + ")";
}
+
+ /**
+ * Convert display name source to string.
+ *
+ * @param source The display name source.
+ * @return The display name source in string format.
+ */
+ @NonNull
+ public static String displayNameSourceToString(
+ @SubscriptionManager.SimDisplayNameSource int source) {
+ switch (source) {
+ case SubscriptionManager.NAME_SOURCE_UNKNOWN: return "UNKNOWN";
+ case SubscriptionManager.NAME_SOURCE_CARRIER_ID: return "CARRIER_ID";
+ case SubscriptionManager.NAME_SOURCE_SIM_SPN: return "SIM_SPN";
+ case SubscriptionManager.NAME_SOURCE_USER_INPUT: return "USER_INPUT";
+ case SubscriptionManager.NAME_SOURCE_CARRIER: return "CARRIER";
+ case SubscriptionManager.NAME_SOURCE_SIM_PNN: return "SIM_PNN";
+ default:
+ return "UNKNOWN(" + source + ")";
+ }
+ }
+
+ /**
+ * Convert subscription type to string.
+ *
+ * @param type The subscription type.
+ * @return The subscription type in string format.
+ */
+ @NonNull
+ public static String subscriptionTypeToString(@SubscriptionManager.SubscriptionType int type) {
+ switch (type) {
+ case SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM: return "LOCAL_SIM";
+ case SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM: return "REMOTE_SIM";
+ default:
+ return "UNKNOWN(" + type + ")";
+ }
+ }
+
+ /**
+ * Convert usage setting to string.
+ *
+ * @param usageSetting Usage setting.
+ * @return The usage setting in string format.
+ */
+ @NonNull
+ public static String usageSettingToString(@SubscriptionManager.UsageSetting int usageSetting) {
+ switch (usageSetting) {
+ case SubscriptionManager.USAGE_SETTING_UNKNOWN: return "UNKNOWN";
+ case SubscriptionManager.USAGE_SETTING_DEFAULT: return "DEFAULT";
+ case SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC: return "VOICE_CENTRIC";
+ case SubscriptionManager.USAGE_SETTING_DATA_CENTRIC: return "DATA_CENTRIC";
+ default:
+ return "UNKNOWN(" + usageSetting + ")";
+ }
+ }
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2a1efed63dd2..0622612bb064 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3348,27 +3348,42 @@ public class CarrierConfigManager {
public static final String KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING =
"carrier_qualified_networks_service_class_override_string";
/**
- * A list of 4 LTE RSCP thresholds above which a signal level is considered POOR,
+ * A list of 4 WCDMA RSCP thresholds above which a signal level is considered POOR,
* MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
*
* Note that the min and max thresholds are fixed at -120 and -24, as set in 3GPP TS 27.007
* section 8.69.
* <p>
- * See SignalStrength#MAX_WCDMA_RSCP and SignalStrength#MIN_WDCMA_RSCP. Any signal level outside
- * these boundaries is considered invalid.
+ * See CellSignalStrengthWcdma#WCDMA_RSCP_MAX and CellSignalStrengthWcdma#WCDMA_RSCP_MIN.
+ * Any signal level outside these boundaries is considered invalid.
* @hide
*/
public static final String KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY =
"wcdma_rscp_thresholds_int_array";
/**
+ * A list of 4 WCDMA ECNO thresholds above which a signal level is considered POOR,
+ * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
+ *
+ * Note that the min and max thresholds are fixed at -24 and 1, as set in 3GPP TS 25.215
+ * section 5.1.5.
+ * Any signal level outside these boundaries is considered invalid.
+ * <p>
+ *
+ * The default value is {@code {-24, -14, -6, 1}}.
+ * @hide
+ */
+ public static final String KEY_WCDMA_ECNO_THRESHOLDS_INT_ARRAY =
+ "wcdma_ecno_thresholds_int_array";
+
+ /**
* The default measurement to use for signal strength reporting. If this is not specified, the
* RSSI is used.
* <p>
* e.g.) To use RSCP by default, set the value to "rscp". The signal strength level will
* then be determined by #KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY
* <p>
- * Currently this supports the value "rscp" and "rssi".
+ * Currently this supports the value "rscp","rssi" and "ecno".
* @hide
*/
// FIXME: this key and related keys must not be exposed without a consistent philosophy for
@@ -9142,6 +9157,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL, false);
sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+ // Boundaries: [-140 dBm, -44 dBm]
new int[] {
-128, /* SIGNAL_STRENGTH_POOR */
-118, /* SIGNAL_STRENGTH_MODERATE */
@@ -9149,6 +9165,7 @@ public class CarrierConfigManager {
-98, /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putIntArray(KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
+ // Boundaries: [-34 dB, 3 dB]
new int[] {
-20, /* SIGNAL_STRENGTH_POOR */
-17, /* SIGNAL_STRENGTH_MODERATE */
@@ -9156,6 +9173,7 @@ public class CarrierConfigManager {
-11 /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putIntArray(KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
+ // Boundaries: [-20 dBm, 30 dBm]
new int[] {
-3, /* SIGNAL_STRENGTH_POOR */
1, /* SIGNAL_STRENGTH_MODERATE */
@@ -9163,12 +9181,23 @@ public class CarrierConfigManager {
13 /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putIntArray(KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
+ // Boundaries: [-120 dBm, -25 dBm]
new int[] {
-115, /* SIGNAL_STRENGTH_POOR */
-105, /* SIGNAL_STRENGTH_MODERATE */
-95, /* SIGNAL_STRENGTH_GOOD */
-85 /* SIGNAL_STRENGTH_GREAT */
});
+ // TODO(b/249896055): On enabling ECNO measurement part for Signal Bar level indication
+ // system functionality,below values to be rechecked.
+ sDefaults.putIntArray(KEY_WCDMA_ECNO_THRESHOLDS_INT_ARRAY,
+ // Boundaries: [-24 dBm, 1 dBm]
+ new int[] {
+ -24, /* SIGNAL_STRENGTH_POOR */
+ -14, /* SIGNAL_STRENGTH_MODERATE */
+ -6, /* SIGNAL_STRENGTH_GOOD */
+ 1 /* SIGNAL_STRENGTH_GREAT */
+ });
sDefaults.putIntArray(KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY,
// Boundaries: [-140 dB, -44 dB]
new int[] {
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 4d58b22080cd..0d3c80fd8887 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -96,13 +96,6 @@ public class SubscriptionInfo implements Parcelable {
private final CharSequence mCarrierName;
/**
- * The subscription carrier id.
- *
- * @see TelephonyManager#getSimCarrierId()
- */
- private final int mCarrierId;
-
- /**
* The source of the {@link #mDisplayName}.
*/
@SimDisplayNameSource
@@ -127,12 +120,6 @@ public class SubscriptionInfo implements Parcelable {
private final int mDataRoaming;
/**
- * SIM icon bitmap cache.
- */
- @Nullable
- private Bitmap mIconBitmap;
-
- /**
* Mobile Country Code.
*/
@Nullable
@@ -157,15 +144,16 @@ public class SubscriptionInfo implements Parcelable {
private final String[] mHplmns;
/**
- * ISO Country code for the subscription's provider.
+ * Whether the subscription is from eSIM.
*/
- @NonNull
- private final String mCountryIso;
+ private final boolean mIsEmbedded;
/**
- * Whether the subscription is from eSIM.
+ * The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
+ * EID for an eUICC card.
*/
- private final boolean mIsEmbedded;
+ @NonNull
+ private final String mCardString;
/**
* The access rules for this subscription, if it is embedded and defines any. This does not
@@ -182,18 +170,6 @@ public class SubscriptionInfo implements Parcelable {
private final UiccAccessRule[] mCarrierConfigAccessRules;
/**
- * The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
- * EID for an eUICC card.
- */
- @NonNull
- private final String mCardString;
-
- /**
- * The card ID of the SIM card. This maps uniquely to {@link #mCardString}.
- */
- private final int mCardId;
-
- /**
* Whether the subscription is opportunistic.
*/
private final boolean mIsOpportunistic;
@@ -207,18 +183,17 @@ public class SubscriptionInfo implements Parcelable {
private final ParcelUuid mGroupUuid;
/**
- * A package name that specifies who created the group. Empty if not available.
+ * ISO Country code for the subscription's provider.
*/
@NonNull
- private final String mGroupOwner;
+ private final String mCountryIso;
/**
- * Whether group of the subscription is disabled. This is only useful if it's a grouped
- * opportunistic subscription. In this case, if all primary (non-opportunistic) subscriptions
- * in the group are deactivated (unplugged pSIM or deactivated eSIM profile), we should disable
- * this opportunistic subscription.
+ * The subscription carrier id.
+ *
+ * @see TelephonyManager#getSimCarrierId()
*/
- private final boolean mIsGroupDisabled;
+ private final int mCarrierId;
/**
* The profile class populated from the profile metadata if present. Otherwise,
@@ -236,6 +211,12 @@ public class SubscriptionInfo implements Parcelable {
private final int mType;
/**
+ * A package name that specifies who created the group. Empty if not available.
+ */
+ @NonNull
+ private final String mGroupOwner;
+
+ /**
* Whether uicc applications are configured to enable or disable.
* By default it's true.
*/
@@ -252,6 +233,27 @@ public class SubscriptionInfo implements Parcelable {
@UsageSetting
private final int mUsageSetting;
+ // Below are the fields that do not exist in the database.
+
+ /**
+ * SIM icon bitmap cache.
+ */
+ @Nullable
+ private Bitmap mIconBitmap;
+
+ /**
+ * The card ID of the SIM card. This maps uniquely to {@link #mCardString}.
+ */
+ private final int mCardId;
+
+ /**
+ * Whether group of the subscription is disabled. This is only useful if it's a grouped
+ * opportunistic subscription. In this case, if all primary (non-opportunistic) subscriptions
+ * in the group are deactivated (unplugged pSIM or deactivated eSIM profile), we should disable
+ * this opportunistic subscription.
+ */
+ private final boolean mIsGroupDisabled;
+
/**
* @hide
*
@@ -665,7 +667,8 @@ public class SubscriptionInfo implements Parcelable {
*/
@NonNull
public List<String> getEhplmns() {
- return mEhplmns == null ? Collections.emptyList() : Arrays.asList(mEhplmns);
+ return Collections.unmodifiableList(mEhplmns == null
+ ? Collections.emptyList() : Arrays.asList(mEhplmns));
}
/**
@@ -673,7 +676,8 @@ public class SubscriptionInfo implements Parcelable {
*/
@NonNull
public List<String> getHplmns() {
- return mHplmns == null ? Collections.emptyList() : Arrays.asList(mHplmns);
+ return Collections.unmodifiableList(mHplmns == null
+ ? Collections.emptyList() : Arrays.asList(mHplmns));
}
/**
@@ -777,7 +781,7 @@ public class SubscriptionInfo implements Parcelable {
if (mCarrierConfigAccessRules != null) {
merged.addAll(Arrays.asList(mCarrierConfigAccessRules));
}
- return merged.isEmpty() ? null : merged;
+ return merged.isEmpty() ? null : Collections.unmodifiableList(merged);
}
/**
@@ -957,69 +961,75 @@ public class SubscriptionInfo implements Parcelable {
public String toString() {
String iccIdToPrint = givePrintableIccid(mIccId);
String cardStringToPrint = givePrintableIccid(mCardString);
- return "{id=" + mId + " iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
- + " carrierId=" + mCarrierId + " displayName=" + mDisplayName
- + " carrierName=" + mCarrierName + " nameSource=" + mDisplayNameSource
- + " iconTint=" + mIconTint
- + " number=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumber)
- + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc=" + mMcc
- + " mnc=" + mMnc + " countryIso=" + mCountryIso + " isEmbedded=" + mIsEmbedded
- + " nativeAccessRules=" + Arrays.toString(mNativeAccessRules)
- + " cardString=" + cardStringToPrint + " cardId=" + mCardId
+ return "[SubscriptionInfo: id=" + mId
+ + " iccId=" + iccIdToPrint
+ + " simSlotIndex=" + mSimSlotIndex
+ " portIndex=" + mPortIndex
- + " isOpportunistic=" + mIsOpportunistic + " groupUuid=" + mGroupUuid
+ + " isEmbedded=" + mIsEmbedded
+ + " carrierId=" + mCarrierId
+ + " displayName=" + mDisplayName
+ + " carrierName=" + mCarrierName
+ + " isOpportunistic=" + mIsOpportunistic
+ + " groupUuid=" + mGroupUuid
+ + " groupOwner=" + mGroupOwner
+ " isGroupDisabled=" + mIsGroupDisabled
- + " profileClass=" + mProfileClass
+ + " displayNameSource="
+ + TelephonyUtils.displayNameSourceToString(mDisplayNameSource)
+ + " iconTint=" + mIconTint
+ + " number=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumber)
+ + " dataRoaming=" + mDataRoaming
+ + " mcc=" + mMcc
+ + " mnc=" + mMnc
+ " ehplmns=" + Arrays.toString(mEhplmns)
+ " hplmns=" + Arrays.toString(mHplmns)
- + " mType=" + mType
- + " groupOwner=" + mGroupOwner
+ + " cardString=" + cardStringToPrint
+ + " cardId=" + mCardId
+ + " nativeAccessRules=" + Arrays.toString(mNativeAccessRules)
+ " carrierConfigAccessRules=" + Arrays.toString(mCarrierConfigAccessRules)
+ + " countryIso=" + mCountryIso
+ + " profileClass=" + mProfileClass
+ + " mType=" + TelephonyUtils.subscriptionTypeToString(mType)
+ " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
- + " usageSetting=" + mUsageSetting + "}";
+ + " usageSetting=" + TelephonyUtils.usageSettingToString(mUsageSetting)
+ + "]";
}
@Override
- public int hashCode() {
- return Objects.hash(mId, mSimSlotIndex, mDisplayNameSource, mIconTint, mDataRoaming,
- mIsEmbedded, mIsOpportunistic, mGroupUuid, mIccId, mNumber, mMcc, mMnc, mCountryIso,
- mCardString, mCardId, mDisplayName, mCarrierName,
- Arrays.hashCode(mNativeAccessRules), mIsGroupDisabled, mCarrierId, mProfileClass,
- mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting);
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SubscriptionInfo that = (SubscriptionInfo) o;
+ return mId == that.mId && mSimSlotIndex == that.mSimSlotIndex
+ && mDisplayNameSource == that.mDisplayNameSource && mIconTint == that.mIconTint
+ && mDataRoaming == that.mDataRoaming && mIsEmbedded == that.mIsEmbedded
+ && mIsOpportunistic == that.mIsOpportunistic && mCarrierId == that.mCarrierId
+ && mProfileClass == that.mProfileClass && mType == that.mType
+ && mAreUiccApplicationsEnabled == that.mAreUiccApplicationsEnabled
+ && mPortIndex == that.mPortIndex && mUsageSetting == that.mUsageSetting
+ && mCardId == that.mCardId && mIsGroupDisabled == that.mIsGroupDisabled
+ && mIccId.equals(that.mIccId) && mDisplayName.equals(that.mDisplayName)
+ && mCarrierName.equals(that.mCarrierName) && mNumber.equals(that.mNumber)
+ && Objects.equals(mMcc, that.mMcc) && Objects.equals(mMnc,
+ that.mMnc) && Arrays.equals(mEhplmns, that.mEhplmns)
+ && Arrays.equals(mHplmns, that.mHplmns) && mCardString.equals(
+ that.mCardString) && Arrays.equals(mNativeAccessRules,
+ that.mNativeAccessRules) && Arrays.equals(mCarrierConfigAccessRules,
+ that.mCarrierConfigAccessRules) && Objects.equals(mGroupUuid, that.mGroupUuid)
+ && mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner);
}
@Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null || getClass() != obj.getClass()) return false;
- SubscriptionInfo toCompare = (SubscriptionInfo) obj;
- return mId == toCompare.mId
- && mSimSlotIndex == toCompare.mSimSlotIndex
- && mDisplayNameSource == toCompare.mDisplayNameSource
- && mIconTint == toCompare.mIconTint
- && mDataRoaming == toCompare.mDataRoaming
- && mIsEmbedded == toCompare.mIsEmbedded
- && mIsOpportunistic == toCompare.mIsOpportunistic
- && mIsGroupDisabled == toCompare.mIsGroupDisabled
- && mAreUiccApplicationsEnabled == toCompare.mAreUiccApplicationsEnabled
- && mCarrierId == toCompare.mCarrierId
- && Objects.equals(mGroupUuid, toCompare.mGroupUuid)
- && Objects.equals(mIccId, toCompare.mIccId)
- && Objects.equals(mNumber, toCompare.mNumber)
- && Objects.equals(mMcc, toCompare.mMcc)
- && Objects.equals(mMnc, toCompare.mMnc)
- && Objects.equals(mCountryIso, toCompare.mCountryIso)
- && Objects.equals(mCardString, toCompare.mCardString)
- && Objects.equals(mCardId, toCompare.mCardId)
- && mPortIndex == toCompare.mPortIndex
- && Objects.equals(mGroupOwner, toCompare.mGroupOwner)
- && TextUtils.equals(mDisplayName, toCompare.mDisplayName)
- && TextUtils.equals(mCarrierName, toCompare.mCarrierName)
- && Arrays.equals(mNativeAccessRules, toCompare.mNativeAccessRules)
- && mProfileClass == toCompare.mProfileClass
- && Arrays.equals(mEhplmns, toCompare.mEhplmns)
- && Arrays.equals(mHplmns, toCompare.mHplmns)
- && mUsageSetting == toCompare.mUsageSetting;
+ public int hashCode() {
+ int result = Objects.hash(mId, mIccId, mSimSlotIndex, mDisplayName, mCarrierName,
+ mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mIsEmbedded,
+ mCardString, mIsOpportunistic, mGroupUuid, mCountryIso, mCarrierId, mProfileClass,
+ mType, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting, mCardId,
+ mIsGroupDisabled);
+ result = 31 * result + Arrays.hashCode(mEhplmns);
+ result = 31 * result + Arrays.hashCode(mHplmns);
+ result = 31 * result + Arrays.hashCode(mNativeAccessRules);
+ result = 31 * result + Arrays.hashCode(mCarrierConfigAccessRules);
+ return result;
}
/**
@@ -1031,7 +1041,7 @@ public class SubscriptionInfo implements Parcelable {
/**
* The subscription id.
*/
- private int mId = 0;
+ private int mId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
/**
* The ICCID of the SIM that is associated with this subscription, empty if unknown.
@@ -1064,7 +1074,7 @@ public class SubscriptionInfo implements Parcelable {
* The source of the display name.
*/
@SimDisplayNameSource
- private int mDisplayNameSource = SubscriptionManager.NAME_SOURCE_CARRIER_ID;
+ private int mDisplayNameSource = SubscriptionManager.NAME_SOURCE_UNKNOWN;
/**
* The color to be used for tinting the icon when displaying to the user.
@@ -1141,7 +1151,7 @@ public class SubscriptionInfo implements Parcelable {
/**
* The card ID of the SIM card which contains the subscription.
*/
- private int mCardId = -1;
+ private int mCardId = TelephonyManager.UNINITIALIZED_CARD_ID;
/**
* Whether the subscription is opportunistic or not.
@@ -1205,7 +1215,7 @@ public class SubscriptionInfo implements Parcelable {
/**
* the port index of the Uicc card.
*/
- private int mPortIndex = 0;
+ private int mPortIndex = TelephonyManager.INVALID_PORT_INDEX;
/**
* Subscription's preferred usage setting.
@@ -1433,9 +1443,9 @@ public class SubscriptionInfo implements Parcelable {
}
/**
- * Set the ISO Country code for the subscription's provider.
+ * Set the ISO country code for the subscription's provider.
*
- * @param countryIso The ISO Country code for the subscription's provider.
+ * @param countryIso The ISO country code for the subscription's provider.
* @return The builder.
*/
@NonNull
@@ -1592,7 +1602,7 @@ public class SubscriptionInfo implements Parcelable {
* Set the carrier certificates for this subscription that are saved in carrier configs.
* This does not include access rules from the Uicc, whether embedded or non-embedded.
*
- * @param carrierConfigAccessRules The carrier certificates for this subscription
+ * @param carrierConfigAccessRules The carrier certificates for this subscription.
* @return The builder.
*/
@NonNull
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 193c2c143f98..6189b49bcf79 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -589,6 +589,12 @@ public class SubscriptionManager {
public static final String NAME_SOURCE = SimInfo.COLUMN_NAME_SOURCE;
/**
+ * The name_source is unknown. (for initialization)
+ * @hide
+ */
+ public static final int NAME_SOURCE_UNKNOWN = SimInfo.NAME_SOURCE_UNKNOWN;
+
+ /**
* The name_source is from the carrier id.
* @hide
*/
@@ -623,6 +629,7 @@ public class SubscriptionManager {
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"NAME_SOURCE_"},
value = {
+ NAME_SOURCE_UNKNOWN,
NAME_SOURCE_CARRIER_ID,
NAME_SOURCE_SIM_SPN,
NAME_SOURCE_USER_INPUT,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index a27b2da1a19e..f810fbb2e3ec 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.ime
-import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
@@ -47,6 +47,7 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
+ tapl.workspace.switchToOverview().dismissAllTasks()
testApp.launchViaIntent(wmHelper)
testApp.openIME(wmHelper)
this.setRotation(testSpec.startRotation)
@@ -61,7 +62,7 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
}
/** {@inheritDoc} */
- @FlakyTest(bugId = 251214932)
+ @Presubmit
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
// depends on how much of the animation transactions are sent to SF at once
@@ -79,23 +80,23 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
}
/** {@inheritDoc} */
- @FlakyTest(bugId = 251214932)
+ @Presubmit
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
val component = ComponentNameMatcher("", "RecentTaskScreenshotSurface")
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(
ignoreWindows =
- listOf(
- ComponentNameMatcher.SPLASH_SCREEN,
- ComponentNameMatcher.SNAPSHOT,
- component
- )
+ listOf(
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT,
+ component
+ )
)
}
}
- @FlakyTest(bugId = 251214932)
+ @Presubmit
@Test
fun launcherWindowBecomesInvisible() {
testSpec.assertWm {
@@ -105,11 +106,11 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
}
}
- @FlakyTest(bugId = 251214932)
+ @Presubmit
@Test
fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible()
- @FlakyTest(bugId = 251214932)
+ @Presubmit
@Test
fun imeAppWindowIsAlwaysVisible() {
// the app starts visible in live tile, and stays visible for the duration of entering
@@ -119,13 +120,13 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
testSpec.assertWm { this.isAppWindowVisible(testApp) }
}
- @FlakyTest(bugId = 251214932)
+ @Presubmit
@Test
fun imeLayerBecomesVisible() {
testSpec.assertLayers { this.isVisible(ComponentNameMatcher.IME) }
}
- @FlakyTest(bugId = 251214932)
+ @Presubmit
@Test
fun appLayerReplacesLauncher() {
testSpec.assertLayers {
@@ -137,60 +138,6 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
}
}
- @FlakyTest(bugId = 251214932)
- @Test
- override fun navBarLayerPositionAtStartAndEnd() {
- super.navBarLayerPositionAtStartAndEnd()
- }
-
- @FlakyTest(bugId = 251214932)
- @Test
- override fun navBarWindowIsAlwaysVisible() {
- super.navBarWindowIsAlwaysVisible()
- }
-
- @FlakyTest(bugId = 251214932)
- @Test
- override fun statusBarLayerIsVisibleAtStartAndEnd() {
- super.statusBarLayerIsVisibleAtStartAndEnd()
- }
-
- @FlakyTest(bugId = 251214932)
- @Test
- override fun entireScreenCovered() {
- super.entireScreenCovered()
- }
-
- @FlakyTest(bugId = 251214932)
- @Test
- override fun navBarLayerIsVisibleAtStartAndEnd() {
- super.navBarLayerIsVisibleAtStartAndEnd()
- }
-
- @FlakyTest(bugId = 251214932)
- @Test
- override fun statusBarLayerPositionAtStartAndEnd() {
- super.statusBarLayerPositionAtStartAndEnd()
- }
-
- @FlakyTest(bugId = 251214932)
- @Test
- override fun statusBarWindowIsAlwaysVisible() {
- super.statusBarWindowIsAlwaysVisible()
- }
-
- @FlakyTest(bugId = 251214932)
- @Test
- override fun taskBarLayerIsVisibleAtStartAndEnd() {
- super.taskBarLayerIsVisibleAtStartAndEnd()
- }
-
- @FlakyTest(bugId = 251214932)
- @Test
- override fun taskBarWindowIsAlwaysVisible() {
- super.taskBarWindowIsAlwaysVisible()
- }
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
index 7ccfeb7f6edd..73e6d223f824 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -81,87 +81,101 @@ class OpenAppColdFromIcon(testSpec: FlickerTestParameter) :
}
/** {@inheritDoc} */
- @Postsubmit @Test override fun appWindowAsTopWindowAtEnd() = super.appWindowAsTopWindowAtEnd()
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowAsTopWindowAtEnd() = super.appWindowAsTopWindowAtEnd()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest(bugId = 240916028)
@Test
override fun appWindowReplacesLauncherAsTopWindow() =
super.appWindowReplacesLauncherAsTopWindow()
/** {@inheritDoc} */
- @Postsubmit @Test override fun appLayerBecomesVisible() = super.appLayerBecomesVisible()
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appLayerBecomesVisible() = super.appLayerBecomesVisible()
/** {@inheritDoc} */
- @Postsubmit @Test override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
/** {@inheritDoc} */
- @Postsubmit @Test override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
/** {@inheritDoc} */
- @Postsubmit @Test override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
/** {@inheritDoc} */
- @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
/** {@inheritDoc} */
- @Postsubmit @Test override fun focusChanges() = super.focusChanges()
+ @FlakyTest(bugId = 240916028) @Test override fun focusChanges() = super.focusChanges()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest(bugId = 240916028)
@Test
override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest(bugId = 240916028)
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest(bugId = 240916028)
@Test
override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest(bugId = 240916028)
@Test
override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest(bugId = 240916028)
@Test
override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest(bugId = 240916028)
@Test
override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest(bugId = 240916028)
@Test
override fun statusBarLayerIsVisibleAtStartAndEnd() =
super.statusBarLayerIsVisibleAtStartAndEnd()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest(bugId = 240916028)
@Test
override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest(bugId = 240916028)
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest(bugId = 240916028)
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
/** {@inheritDoc} */
- @Postsubmit @Test override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd()
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd()
companion object {
/**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 46186bcb5edc..bc1f0d18d8f4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
@@ -104,7 +103,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(testSpec: FlickerTestParameter) : B
* Checks that the transition starts with [testApp2]'s layers filling/covering exactly the
* entirety of the display.
*/
- @FlakyTest(bugId = 250520840)
+ @Presubmit
@Test
open fun startsWithApp2LayersCoverFullScreen() {
testSpec.assertLayersStart {
@@ -236,15 +235,6 @@ open class QuickSwitchBetweenTwoAppsBackTest(testSpec: FlickerTestParameter) : B
}
}
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
-
- @FlakyTest(bugId = 250518877)
- @Test
- override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
-
companion object {
private var startDisplayBounds = Rect.EMPTY
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
index 7a1350e5bbfa..f988bb2f1bf1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
@@ -23,6 +23,7 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.navBarWindowIsVisibleAtStartAndEnd
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -61,8 +62,8 @@ open class QuickSwitchBetweenTwoAppsBackTest_ShellTransit(testSpec: FlickerTestP
override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
/**
- * Checks that [ComponentMatcher.NAV_BAR] window is visible and above the app windows at the
- * start and end of the WM trace
+ * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at
+ * the start and end of the WM trace
*/
@Presubmit
@Test
@@ -71,8 +72,18 @@ open class QuickSwitchBetweenTwoAppsBackTest_ShellTransit(testSpec: FlickerTestP
testSpec.navBarWindowIsVisibleAtStartAndEnd()
}
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 250520840)
+ @Test
+ override fun startsWithApp2LayersCoverFullScreen() =
+ super.startsWithApp2LayersCoverFullScreen()
+
@FlakyTest(bugId = 246284708)
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest(bugId = 250518877)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index 0a21044f6dda..7e4504bc7a48 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
@@ -116,7 +115,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(testSpec: FlickerTestParameter)
* Checks that the transition starts with [testApp1]'s layers filling/covering exactly the
* entirety of the display.
*/
- @FlakyTest(bugId = 250522691)
+ @Presubmit
@Test
open fun startsWithApp1LayersCoverFullScreen() {
testSpec.assertLayersStart {
@@ -255,10 +254,6 @@ open class QuickSwitchBetweenTwoAppsForwardTest(testSpec: FlickerTestParameter)
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
- @FlakyTest(bugId = 250518877)
- @Test
- override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
-
companion object {
private var startDisplayBounds = Rect.EMPTY
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
index 03647c9801c7..cc954ab6ee5d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
@@ -23,6 +23,7 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.navBarWindowIsVisibleAtStartAndEnd
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -62,8 +63,8 @@ open class QuickSwitchBetweenTwoAppsForwardTest_ShellTransit(testSpec: FlickerTe
override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
/**
- * Checks that [ComponentMatcher.NAV_BAR] window is visible and above the app windows at the
- * start and end of the WM trace
+ * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at
+ * the start and end of the WM trace
*/
@Presubmit
@Test
@@ -76,4 +77,13 @@ open class QuickSwitchBetweenTwoAppsForwardTest_ShellTransit(testSpec: FlickerTe
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest(bugId = 250518877)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ @FlakyTest(bugId = 250522691)
+ @Test
+ override fun startsWithApp1LayersCoverFullScreen() =
+ super.startsWithApp1LayersCoverFullScreen()
}
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
index 06a96dfb8176..8aaf18af0664 100644
--- a/tests/Input/src/com/android/test/input/InputDeviceTest.java
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -87,6 +87,7 @@ public class InputDeviceTest {
.setHasSensor(true)
.setHasBattery(true)
.setCountryCode(InputDeviceCountryCode.INTERNATIONAL)
+ .setSupportsUsi(true)
.build();
Parcel parcel = Parcel.obtain();
diff --git a/tests/TrustTests/Android.bp b/tests/TrustTests/Android.bp
index 77f98e88f1eb..a1b888aef934 100644
--- a/tests/TrustTests/Android.bp
+++ b/tests/TrustTests/Android.bp
@@ -24,7 +24,7 @@ android_test {
static_libs: [
"androidx.test.rules",
"androidx.test.ext.junit",
- "androidx.test.uiautomator",
+ "androidx.test.uiautomator_uiautomator",
"mockito-target-minus-junit4",
"servicestests-utils",
"truth-prebuilt",
diff --git a/tools/processors/immutability/Android.bp b/tools/processors/immutability/Android.bp
index fe97a903bff8..2ce785f10a2c 100644
--- a/tools/processors/immutability/Android.bp
+++ b/tools/processors/immutability/Android.bp
@@ -67,6 +67,7 @@ java_test_host {
"--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
],
+ test_config_template: "AndroidTestTemplate.xml",
}
filegroup {
diff --git a/tools/processors/immutability/AndroidTestTemplate.xml b/tools/processors/immutability/AndroidTestTemplate.xml
new file mode 100644
index 000000000000..b9cf62f5ca5b
--- /dev/null
+++ b/tools/processors/immutability/AndroidTestTemplate.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<!-- This test config file is auto-generated. -->
+<configuration description="Runs {MODULE}">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-unit-tests" />
+ <option name="config-descriptor:metadata" key="component" value="{MODULE}" />
+
+ {EXTRA_CONFIGS}
+
+ <test class="com.android.tradefed.testtype.IsolatedHostTest" >
+ <option name="jar" value="{MODULE}.jar" />
+ <option name="java-flags" value="--add-modules=jdk.compiler"/>
+ <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED"/>
+ <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED"/>
+ <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED"/>
+ <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"/>
+ </test>
+</configuration>
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
index 0fb062f280e3..0b619488c49c 100644
--- a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
@@ -39,7 +39,7 @@ fun main(args: Array<String>) {
kotlin.system.exitProcess(2)
}
- val ancestorCollector = AncestorCollector(Opcodes.ASM7, null)
+ val ancestorCollector = AncestorCollector(Opcodes.ASM9, null)
for (entry in zipFile.entries()) {
if (entry.name.endsWith(".class")) {
diff --git a/tools/traceinjection/src/com/android/traceinjection/TraceInjectionClassVisitor.java b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionClassVisitor.java
index 863f976b8aff..67c5561f348d 100644
--- a/tools/traceinjection/src/com/android/traceinjection/TraceInjectionClassVisitor.java
+++ b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionClassVisitor.java
@@ -28,7 +28,7 @@ public class TraceInjectionClassVisitor extends ClassVisitor {
private final TraceInjectionConfiguration mParams;
public TraceInjectionClassVisitor(ClassVisitor classVisitor,
TraceInjectionConfiguration params) {
- super(Opcodes.ASM7, classVisitor);
+ super(Opcodes.ASM9, classVisitor);
mParams = params;
}
diff --git a/tools/traceinjection/src/com/android/traceinjection/TraceInjectionMethodAdapter.java b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionMethodAdapter.java
index c2bbddcb5668..91e987dc72ee 100644
--- a/tools/traceinjection/src/com/android/traceinjection/TraceInjectionMethodAdapter.java
+++ b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionMethodAdapter.java
@@ -61,7 +61,7 @@ public class TraceInjectionMethodAdapter extends AdviceAdapter {
public TraceInjectionMethodAdapter(MethodVisitor methodVisitor, int access,
String name, String descriptor, TraceInjectionConfiguration params) {
- super(Opcodes.ASM7, methodVisitor, access, name, descriptor);
+ super(Opcodes.ASM9, methodVisitor, access, name, descriptor);
mParams = params;
mIsConstructor = "<init>".equals(name);
}
@@ -157,7 +157,7 @@ public class TraceInjectionMethodAdapter extends AdviceAdapter {
class TracingAnnotationVisitor extends AnnotationVisitor {
TracingAnnotationVisitor(AnnotationVisitor annotationVisitor) {
- super(Opcodes.ASM7, annotationVisitor);
+ super(Opcodes.ASM9, annotationVisitor);
}
@Override