summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp3
-rw-r--r--ApiDocs.bp4
-rw-r--r--PREUPLOAD.cfg1
-rw-r--r--StubLibraries.bp2
-rw-r--r--apct-tests/perftests/core/Android.bp23
-rw-r--r--apct-tests/perftests/core/AndroidManifest.xml12
-rw-r--r--apct-tests/perftests/core/src/android/database/CrossProcessCursorPerfTest.java89
-rw-r--r--apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java26
-rw-r--r--apct-tests/perftests/core/src/android/os/SomeProvider.java73
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java3
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java2
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java2
-rw-r--r--apex/media/OWNERS3
-rw-r--r--apex/media/aidl/Android.bp10
-rw-r--r--apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl19
-rw-r--r--apex/media/framework/Android.bp12
-rw-r--r--apex/media/framework/api/module-lib-current.txt14
-rw-r--r--apex/media/framework/java/android/media/BaseMediaParceledListSlice.java215
-rw-r--r--apex/media/framework/java/android/media/MediaParceledListSlice.java101
-rw-r--r--api/current.txt60
-rw-r--r--api/module-lib-current.txt18
-rw-r--r--api/system-current.txt18
-rw-r--r--api/test-current.txt9
-rw-r--r--cmds/statsd/src/atoms.proto57
-rw-r--r--cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp15
-rw-r--r--cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp156
-rw-r--r--core/java/android/app/ActivityManager.java15
-rw-r--r--core/java/android/app/AppOpsManager.java5
-rw-r--r--core/java/android/app/IActivityManager.aidl6
-rw-r--r--core/java/android/app/NotificationHistory.java7
-rw-r--r--core/java/android/app/PendingIntent.java57
-rw-r--r--core/java/android/app/people/IPeopleManager.aidl6
-rw-r--r--core/java/android/app/role/RoleManager.java2
-rw-r--r--core/java/android/bluetooth/BluetoothGatt.java25
-rw-r--r--core/java/android/bluetooth/BluetoothGattCallback.java14
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageManager.java15
-rw-r--r--core/java/android/hardware/Sensor.java1
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java16
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java16
-rw-r--r--core/java/android/hardware/camera2/impl/FrameNumberTracker.java104
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java10
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintSensorProperties.java2
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl4
-rw-r--r--core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl6
-rw-r--r--core/java/android/hardware/input/InputManagerInternal.java22
-rw-r--r--core/java/android/net/LinkProperties.java5
-rw-r--r--core/java/android/net/MacAddress.java2
-rw-r--r--core/java/android/net/RouteInfo.java3
-rw-r--r--core/java/android/os/BatteryStats.java12
-rw-r--r--core/java/android/os/GraphicsEnvironment.java228
-rw-r--r--core/java/android/os/IPowerManager.aidl2
-rw-r--r--core/java/android/os/PowerManager.java21
-rw-r--r--core/java/android/os/ServiceManager.java15
-rw-r--r--core/java/android/os/ServiceManagerNative.java4
-rw-r--r--core/java/android/provider/Settings.java9
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java4
-rw-r--r--core/java/android/util/Log.java2
-rw-r--r--core/java/android/util/imetracing/ImeTracing.java114
-rw-r--r--core/java/android/util/imetracing/ImeTracingClientImpl.java71
-rw-r--r--core/java/android/util/imetracing/ImeTracingServerImpl.java154
-rw-r--r--core/java/android/view/IWindow.aidl6
-rw-r--r--core/java/android/view/IWindowManager.aidl11
-rw-r--r--core/java/android/view/IWindowSession.aidl9
-rw-r--r--core/java/android/view/ImeFocusController.java15
-rw-r--r--core/java/android/view/ImeInsetsSourceConsumer.java16
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java29
-rw-r--r--core/java/android/view/InsetsAnimationControlRunner.java11
-rw-r--r--core/java/android/view/InsetsAnimationThreadControlRunner.java7
-rw-r--r--core/java/android/view/InsetsController.java35
-rw-r--r--core/java/android/view/InsetsFlags.java47
-rw-r--r--core/java/android/view/InsetsSource.java16
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java29
-rw-r--r--core/java/android/view/InsetsSourceControl.java22
-rw-r--r--core/java/android/view/InsetsState.java15
-rw-r--r--core/java/android/view/OnReceiveContentCallback.java377
-rw-r--r--core/java/android/view/View.java150
-rw-r--r--core/java/android/view/ViewRootImpl.java89
-rw-r--r--core/java/android/view/WindowManager.java11
-rw-r--r--core/java/android/view/WindowManagerImpl.java9
-rw-r--r--core/java/android/view/WindowlessWindowManager.java10
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java40
-rw-r--r--core/java/android/view/inputmethod/EditorInfo.java27
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java98
-rw-r--r--core/java/android/widget/Editor.java11
-rw-r--r--core/java/android/widget/RichContentReceiver.java234
-rw-r--r--core/java/android/widget/TEST_MAPPING4
-rw-r--r--core/java/android/widget/TextView.java144
-rw-r--r--core/java/android/widget/TextViewOnReceiveContentCallback.java (renamed from core/java/android/widget/TextViewRichContentReceiver.java)62
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java2
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodDebug.java2
-rw-r--r--core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java16
-rw-r--r--core/java/com/android/internal/inputmethod/StartInputFlags.java6
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java12
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java183
-rw-r--r--core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java259
-rw-r--r--core/java/com/android/internal/os/SystemServerCpuThreadReader.java92
-rw-r--r--core/java/com/android/internal/os/SystemServicePowerCalculator.java42
-rw-r--r--core/java/com/android/internal/view/BaseIWindow.java7
-rw-r--r--core/java/com/android/internal/view/IInputMethodClient.aidl1
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl2
-rw-r--r--core/java/com/android/internal/widget/EditableInputConnection.java27
-rw-r--r--core/jni/android_database_CursorWindow.cpp22
-rw-r--r--core/jni/core_jni_helpers.h6
-rw-r--r--core/proto/OWNERS4
-rw-r--r--core/proto/android/server/peopleservice.proto9
-rw-r--r--core/proto/android/stats/style/style_enums.proto9
-rw-r--r--core/proto/android/telephony/enums.proto47
-rw-r--r--core/proto/android/view/imeinsetssourceconsumer.proto6
-rw-r--r--core/proto/android/view/inputmethod/inputmethodeditortrace.proto16
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--core/res/res/values-af/strings.xml6
-rw-r--r--core/res/res/values-am/strings.xml6
-rw-r--r--core/res/res/values-ar/strings.xml6
-rw-r--r--core/res/res/values-as/strings.xml6
-rw-r--r--core/res/res/values-az/strings.xml6
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml6
-rw-r--r--core/res/res/values-be/strings.xml6
-rw-r--r--core/res/res/values-bg/strings.xml6
-rw-r--r--core/res/res/values-bn/strings.xml9
-rw-r--r--core/res/res/values-bs/strings.xml8
-rw-r--r--core/res/res/values-ca/strings.xml10
-rw-r--r--core/res/res/values-cs/strings.xml6
-rw-r--r--core/res/res/values-da/strings.xml6
-rw-r--r--core/res/res/values-de/strings.xml19
-rw-r--r--core/res/res/values-el/strings.xml6
-rw-r--r--core/res/res/values-en-rAU/strings.xml4
-rw-r--r--core/res/res/values-en-rCA/strings.xml4
-rw-r--r--core/res/res/values-en-rGB/strings.xml4
-rw-r--r--core/res/res/values-en-rIN/strings.xml4
-rw-r--r--core/res/res/values-en-rXC/strings.xml4
-rw-r--r--core/res/res/values-es-rUS/strings.xml6
-rw-r--r--core/res/res/values-es/strings.xml6
-rw-r--r--core/res/res/values-et/strings.xml6
-rw-r--r--core/res/res/values-eu/strings.xml6
-rw-r--r--core/res/res/values-fa/strings.xml6
-rw-r--r--core/res/res/values-fi/strings.xml6
-rw-r--r--core/res/res/values-fr-rCA/strings.xml6
-rw-r--r--core/res/res/values-fr/strings.xml6
-rw-r--r--core/res/res/values-gl/strings.xml6
-rw-r--r--core/res/res/values-gu/strings.xml9
-rw-r--r--core/res/res/values-hi/strings.xml6
-rw-r--r--core/res/res/values-hr/strings.xml6
-rw-r--r--core/res/res/values-hu/strings.xml6
-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.xml6
-rw-r--r--core/res/res/values-iw/strings.xml6
-rw-r--r--core/res/res/values-ja/strings.xml6
-rw-r--r--core/res/res/values-ka/strings.xml6
-rw-r--r--core/res/res/values-kk/strings.xml6
-rw-r--r--core/res/res/values-km/strings.xml6
-rw-r--r--core/res/res/values-kn/strings.xml9
-rw-r--r--core/res/res/values-ko/strings.xml6
-rw-r--r--core/res/res/values-ky/strings.xml6
-rw-r--r--core/res/res/values-lo/strings.xml6
-rw-r--r--core/res/res/values-lt/strings.xml6
-rw-r--r--core/res/res/values-lv/strings.xml6
-rw-r--r--core/res/res/values-mk/strings.xml6
-rw-r--r--core/res/res/values-ml/strings.xml6
-rw-r--r--core/res/res/values-mn/strings.xml6
-rw-r--r--core/res/res/values-mr/strings.xml9
-rw-r--r--core/res/res/values-ms/strings.xml6
-rw-r--r--core/res/res/values-my/strings.xml6
-rw-r--r--core/res/res/values-nb/strings.xml6
-rw-r--r--core/res/res/values-ne/strings.xml9
-rw-r--r--core/res/res/values-nl/strings.xml6
-rw-r--r--core/res/res/values-or/strings.xml6
-rw-r--r--core/res/res/values-pa/strings.xml9
-rw-r--r--core/res/res/values-pl/strings.xml6
-rw-r--r--core/res/res/values-pt-rBR/strings.xml6
-rw-r--r--core/res/res/values-pt-rPT/strings.xml6
-rw-r--r--core/res/res/values-pt/strings.xml6
-rw-r--r--core/res/res/values-ro/strings.xml6
-rw-r--r--core/res/res/values-ru/strings.xml6
-rw-r--r--core/res/res/values-si/strings.xml6
-rw-r--r--core/res/res/values-sk/strings.xml6
-rw-r--r--core/res/res/values-sl/strings.xml6
-rw-r--r--core/res/res/values-sq/strings.xml6
-rw-r--r--core/res/res/values-sr/strings.xml6
-rw-r--r--core/res/res/values-sv/strings.xml6
-rw-r--r--core/res/res/values-sw/strings.xml6
-rw-r--r--core/res/res/values-ta/strings.xml6
-rw-r--r--core/res/res/values-te/strings.xml6
-rw-r--r--core/res/res/values-th/strings.xml8
-rw-r--r--core/res/res/values-tl/strings.xml6
-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.xml9
-rw-r--r--core/res/res/values-uz/strings.xml6
-rw-r--r--core/res/res/values-vi/strings.xml6
-rw-r--r--core/res/res/values-zh-rCN/strings.xml6
-rw-r--r--core/res/res/values-zh-rHK/strings.xml6
-rw-r--r--core/res/res/values-zh-rTW/strings.xml6
-rw-r--r--core/res/res/values-zu/strings.xml6
-rw-r--r--core/tests/coretests/AndroidManifest.xml1
-rw-r--r--core/tests/coretests/src/android/app/NotificationHistoryTest.java10
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java8
-rw-r--r--core/tests/coretests/src/android/view/InsetsFlagsTest.java72
-rw-r--r--core/tests/coretests/src/android/view/InsetsStateTest.java18
-rw-r--r--core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java56
-rw-r--r--core/tests/coretests/src/android/widget/TextViewProcessTextTest.java116
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java152
-rw-r--r--core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java89
-rw-r--r--core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java27
-rw-r--r--data/etc/privapp-permissions-platform.xml8
-rw-r--r--data/fonts/fonts.xml28
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java123
-rw-r--r--errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java105
-rw-r--r--errorprone/tests/res/android/os/Parcel.java57
-rw-r--r--errorprone/tests/res/android/os/Parcelable.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java69
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java99
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java86
-rw-r--r--libs/androidfw/CursorWindow.cpp193
-rw-r--r--libs/androidfw/include/androidfw/CursorWindow.h19
-rw-r--r--media/java/android/media/MediaMetadata.java94
-rw-r--r--media/java/android/media/MediaTranscodeManager.java4
-rw-r--r--media/java/android/media/Ringtone.java2
-rw-r--r--media/java/android/media/RingtoneManager.java15
-rw-r--r--media/java/android/media/session/MediaSession.java4
-rw-r--r--media/java/android/media/tv/ITvInputManager.aidl3
-rw-r--r--media/java/android/media/tv/ITvInputManagerCallback.aidl2
-rw-r--r--media/java/android/media/tv/TvChannelInfo.aidl19
-rw-r--r--media/java/android/media/tv/TvChannelInfo.java148
-rw-r--r--media/java/android/media/tv/TvInputManager.java34
-rw-r--r--non-updatable-api/current.txt60
-rw-r--r--non-updatable-api/module-lib-current.txt12
-rw-r--r--non-updatable-api/system-current.txt14
-rw-r--r--packages/CarSystemUI/res/drawable/car_ic_settings_icon.xml26
-rw-r--r--packages/CarSystemUI/res/drawable/car_ic_user_icon.xml2
-rw-r--r--packages/CarSystemUI/res/layout/car_top_navigation_bar.xml4
-rw-r--r--packages/CarSystemUI/res/layout/system_icons.xml12
-rw-r--r--packages/CarSystemUI/res/values/colors.xml2
-rw-r--r--packages/CarSystemUI/res/values/dimens.xml4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java4
-rw-r--r--packages/InputDevices/res/values-ar/strings.xml3
-rw-r--r--packages/InputDevices/res/values-bn/strings.xml3
-rw-r--r--packages/InputDevices/res/values-cs/strings.xml3
-rw-r--r--packages/InputDevices/res/values-fr-rCA/strings.xml3
-rw-r--r--packages/InputDevices/res/values-fr/strings.xml3
-rw-r--r--packages/InputDevices/res/values-hi/strings.xml3
-rw-r--r--packages/InputDevices/res/values-kk/strings.xml3
-rw-r--r--packages/InputDevices/res/values-km/strings.xml3
-rw-r--r--packages/InputDevices/res/values-mk/strings.xml3
-rw-r--r--packages/InputDevices/res/values-ne/strings.xml3
-rw-r--r--packages/InputDevices/res/values-ru/strings.xml10
-rw-r--r--packages/InputDevices/res/values-sq/strings.xml3
-rw-r--r--packages/InputDevices/res/values-ta/strings.xml3
-rw-r--r--packages/InputDevices/res/values-tl/strings.xml3
-rw-r--r--packages/InputDevices/res/values-vi/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml2
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java4
-rw-r--r--packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java2
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml6
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml21
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml6
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml21
-rw-r--r--packages/SystemUI/res/values-de/strings.xml24
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml6
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml8
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml21
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml21
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml21
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml2
-rw-r--r--packages/SystemUI/res/values-is/strings.xml8
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml8
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml4
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml8
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml21
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml21
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml8
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java13
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java71
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java40
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java30
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java265
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java275
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java19
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java55
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java196
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java73
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java56
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java40
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java244
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java275
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java350
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java349
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java147
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java113
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java66
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java550
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java431
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java19
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java30
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java58
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java107
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java148
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java324
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java350
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java394
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java413
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadKey.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java99
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java145
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaData.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Recents.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java88
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/ViewController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java122
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java87
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java55
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt84
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt59
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java106
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java79
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java129
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java62
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java102
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java11
-rw-r--r--packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java3
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/Tethering.java2
-rw-r--r--packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java1
-rw-r--r--packages/overlays/IconShapePebbleOverlay/res/values/config.xml2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java49
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java (renamed from services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java)118
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java9
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java4
-rw-r--r--services/core/java/com/android/server/NsdService.java2
-rw-r--r--services/core/java/com/android/server/SystemServiceManager.java77
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java25
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java4
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java113
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java263
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java114
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java98
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java18
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java10
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java2
-rw-r--r--services/core/java/com/android/server/connectivity/DataConnectionStats.java8
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java62
-rw-r--r--services/core/java/com/android/server/connectivity/VpnIkev2Utils.java12
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceRepository.java149
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java174
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java7
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java15
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java64
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java104
-rw-r--r--services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java11
-rw-r--r--services/core/java/com/android/server/inputmethod/OWNERS1
-rw-r--r--services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java174
-rw-r--r--services/core/java/com/android/server/media/MediaResourceMonitorService.java14
-rw-r--r--services/core/java/com/android/server/media/MediaShellCommand.java16
-rw-r--r--services/core/java/com/android/server/media/VolumeCtrl.java28
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java34
-rw-r--r--services/core/java/com/android/server/notification/NotificationHistoryDatabase.java22
-rw-r--r--services/core/java/com/android/server/notification/NotificationHistoryManager.java6
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerInternal.java4
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java11
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java7
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java16
-rw-r--r--services/core/java/com/android/server/pm/Settings.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/BasePermission.java3
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java30
-rw-r--r--services/core/java/com/android/server/pm/permission/UidPermissionState.java563
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java25
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java16
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputManagerService.java136
-rw-r--r--services/core/java/com/android/server/utils/XmlName.java31
-rw-r--r--services/core/java/com/android/server/utils/XmlPersistence.java36
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java37
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java54
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java138
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java3
-rw-r--r--services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java20
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java4
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java34
-rw-r--r--services/core/java/com/android/server/wm/PolicyControl.java270
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java72
-rw-r--r--services/core/java/com/android/server/wm/Session.java16
-rw-r--r--services/core/java/com/android/server/wm/Task.java8
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java6
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java63
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java172
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java17
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp31
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp112
-rw-r--r--services/incremental/IncrementalService.cpp2
-rw-r--r--services/people/java/com/android/server/people/PeopleService.java36
-rw-r--r--services/people/java/com/android/server/people/data/ConversationInfo.java73
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java235
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java4
-rw-r--r--services/tests/servicestests/AndroidManifest.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java)50
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java309
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java70
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java20
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java7
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java9
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestIWindow.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java63
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java4
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.java4
-rw-r--r--telecomm/java/android/telecom/PhoneAccount.java12
-rw-r--r--telephony/api/system-current.txt10
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java50
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java83
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java20
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java3
-rw-r--r--telephony/java/android/telephony/ims/ImsService.java58
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl6
-rw-r--r--telephony/java/com/android/ims/ImsFeatureContainer.aidl19
-rw-r--r--telephony/java/com/android/ims/ImsFeatureContainer.java172
-rw-r--r--telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl15
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl17
-rw-r--r--tests/SurfaceViewBufferTests/Android.bp58
-rw-r--r--tests/SurfaceViewBufferTests/AndroidManifest.xml46
-rw-r--r--tests/SurfaceViewBufferTests/AndroidTest.xml35
-rw-r--r--tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp102
-rw-r--r--tests/SurfaceViewBufferTests/res/values/styles.xml25
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt102
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt32
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt185
-rw-r--r--tests/net/Android.bp1
-rw-r--r--tests/net/common/java/android/net/LinkPropertiesTest.java2
-rw-r--r--tests/net/java/android/net/MacAddressTest.java4
-rw-r--r--tests/net/java/com/android/server/connectivity/VpnTest.java65
-rw-r--r--tools/xmlpersistence/Android.bp11
-rw-r--r--tools/xmlpersistence/OWNERS1
-rw-r--r--tools/xmlpersistence/manifest.txt1
-rw-r--r--tools/xmlpersistence/src/main/kotlin/Generator.kt578
-rw-r--r--tools/xmlpersistence/src/main/kotlin/Main.kt45
-rw-r--r--tools/xmlpersistence/src/main/kotlin/Parser.kt248
-rw-r--r--tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt44
-rw-r--r--wifi/api/system-current.txt4
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java2
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java10
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java3
-rw-r--r--wifi/java/android/net/wifi/util/SdkLevelUtil.java14
-rw-r--r--wifi/tests/src/android/net/wifi/WifiConfigurationTest.java3
555 files changed, 15074 insertions, 6886 deletions
diff --git a/Android.bp b/Android.bp
index 59530079d4ad..5af77562de85 100644
--- a/Android.bp
+++ b/Android.bp
@@ -474,6 +474,7 @@ java_library {
"android.hardware.radio-V1.3-java",
"android.hardware.radio-V1.4-java",
"android.hardware.radio-V1.5-java",
+ "android.hardware.radio-V1.6-java",
"android.hardware.thermal-V1.0-java-constants",
"android.hardware.thermal-V1.0-java",
"android.hardware.thermal-V1.1-java",
@@ -1348,7 +1349,7 @@ droidstubs {
},
libs: [
"framework-annotations-lib",
- "android.hardware.radio-V1.5-java",
+ "android.hardware.radio-V1.6-java",
],
check_api: {
current: {
diff --git a/ApiDocs.bp b/ApiDocs.bp
index c82fee0fe8ac..faa0e5dba997 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -65,7 +65,7 @@ stubs_defaults {
"test-base/src/**/*.java",
":opt-telephony-srcs",
":opt-net-voip-srcs",
- ":art-module-public-api-stubs-source",
+ ":art.module.public.api{.public.stubs.source}",
":conscrypt.module.public.api{.public.stubs.source}",
":android_icu4j_public_api_files",
"test-mock/src/**/*.java",
@@ -135,7 +135,7 @@ doc_defaults {
],
knowntags: [
"docs/knowntags.txt",
- ":known-oj-tags",
+ ":art.module.public.api{.doctags}",
],
custom_template: "droiddoc-templates-sdk",
resourcesdir: "docs/html/reference/images/",
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 7a8d1a1caf25..f66d12a69594 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -11,6 +11,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
libs/input/
services/core/jni/
services/incremental/
+ tests/
tools/
[Hook Scripts]
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 852fcc6128c4..a3a209458679 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -47,7 +47,7 @@ stubs_defaults {
"core/java/**/*.logtags",
":opt-telephony-srcs",
":opt-net-voip-srcs",
- ":art-module-public-api-stubs-source",
+ ":art.module.public.api{.public.stubs.source}",
":android_icu4j_public_api_files",
"**/package.html",
],
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index 92dbc263cb34..c5419637dec8 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -1,3 +1,19 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
android_test {
name: "CorePerfTests",
@@ -23,17 +39,14 @@ android_test {
libs: ["android.test.base"],
+ java_resources: [ ":GoogleFontDancingScript", ],
+
data: [":perfetto_artifacts"],
platform_apis: true,
jni_libs: ["libperftestscore_jni"],
- // Use google-fonts/dancing-script for the performance metrics
- // ANDROIDMK TRANSLATION ERROR: Only $(LOCAL_PATH)/.. values are allowed
- // LOCAL_ASSET_DIR := $(TOP)/external/google-fonts/dancing-script
-
test_suites: ["device-tests"],
certificate: "platform",
-
}
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index 833ae63e1125..e0c11cf0e3d5 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -20,7 +20,17 @@
<action android:name="com.android.perftests.core.PERFTEST" />
</intent-filter>
</activity>
- <service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" />
+
+ <service
+ android:name="android.os.SomeService"
+ android:exported="false"
+ android:process=":some_service" />
+
+ <provider
+ android:name="android.os.SomeProvider"
+ android:authorities="android.os.SomeProvider"
+ android:exported="false"
+ android:process=":some_provider" />
<service
android:name="android.view.autofill.MyAutofillService"
diff --git a/apct-tests/perftests/core/src/android/database/CrossProcessCursorPerfTest.java b/apct-tests/perftests/core/src/android/database/CrossProcessCursorPerfTest.java
new file mode 100644
index 000000000000..77654df1273c
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/database/CrossProcessCursorPerfTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.database;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.ContentProviderClient;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.net.Uri;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class CrossProcessCursorPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ /**
+ * Measure transporting a small {@link Cursor}, roughly 1KB in size.
+ */
+ @Test
+ public void timeSmall() throws Exception {
+ time(1);
+ }
+
+ /**
+ * Measure transporting a small {@link Cursor}, roughly 54KB in size.
+ */
+ @Test
+ public void timeMedium() throws Exception {
+ time(100);
+ }
+
+ /**
+ * Measure transporting a small {@link Cursor}, roughly 5.4MB in size.
+ */
+ @Test
+ public void timeLarge() throws Exception {
+ time(10_000);
+ }
+
+ private static final Uri TEST_URI = Uri.parse("content://android.os.SomeProvider/");
+
+ private void time(int count) throws Exception {
+ try (ContentProviderClient client = InstrumentationRegistry.getTargetContext()
+ .getContentResolver().acquireContentProviderClient(TEST_URI)) {
+ // Configure remote side once with data size to return
+ final ContentValues values = new ContentValues();
+ values.put(Intent.EXTRA_INDEX, count);
+ client.update(TEST_URI, values, null);
+
+ // Repeatedly query that data until we reach convergence
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ try (Cursor c = client.query(TEST_URI, null, null, null)) {
+ // Actually walk the returned values to ensure we pull all
+ // data from the remote side
+ while (c.moveToNext()) {
+ assertEquals(c.getPosition(), c.getInt(0));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
index 884745699789..e83c64c37678 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
@@ -27,6 +27,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.Preconditions;
import com.android.perftests.core.R;
import org.junit.Rule;
@@ -73,10 +74,31 @@ public class TypefaceCreatePerfTest {
final AssetManager am = context.getAssets();
while (state.keepRunning()) {
- Typeface face = Typeface.createFromAsset(am, TEST_FONT_NAME);
+ Typeface face = createFromNonAsset(am, TEST_FONT_NAME);
}
}
+ /**
+ * {@link AssetManager#openNonAsset(String)} variant of
+ * {@link Typeface#createFromAsset(AssetManager, String)}.
+ */
+ private static Typeface createFromNonAsset(AssetManager mgr, String path) {
+ Preconditions.checkNotNull(path); // for backward compatibility
+ Preconditions.checkNotNull(mgr);
+
+ Typeface typeface = new Typeface.Builder(mgr, path).build();
+ if (typeface != null) return typeface;
+ // check if the file exists, and throw an exception for backward compatibility
+ //noinspection EmptyTryBlock
+ try (InputStream inputStream = mgr.openNonAsset(path)) {
+ // Purposely empty
+ } catch (IOException e) {
+ throw new RuntimeException("Font asset not found " + path);
+ }
+
+ return Typeface.DEFAULT;
+ }
+
@Test
public void testCreate_fromFile() {
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -90,7 +112,7 @@ public class TypefaceCreatePerfTest {
throw new RuntimeException(e);
}
- try (InputStream in = am.open(TEST_FONT_NAME);
+ try (InputStream in = am.openNonAsset(TEST_FONT_NAME);
OutputStream out = new FileOutputStream(outFile)) {
byte[] buf = new byte[1024];
int n = 0;
diff --git a/apct-tests/perftests/core/src/android/os/SomeProvider.java b/apct-tests/perftests/core/src/android/os/SomeProvider.java
new file mode 100644
index 000000000000..f5e247ea6e93
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/SomeProvider.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.os;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+
+import java.util.Arrays;
+
+public class SomeProvider extends ContentProvider {
+ private Cursor mCursor;
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return mCursor;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ final char[] valueRaw = new char[512];
+ Arrays.fill(valueRaw, '!');
+ final String value = new String(valueRaw);
+
+ final int count = values.getAsInteger(Intent.EXTRA_INDEX);
+ final MatrixCursor cursor = new MatrixCursor(new String[] { "_id", "value" });
+ for (int i = 0; i < count; i++) {
+ MatrixCursor.RowBuilder row = cursor.newRow();
+ row.add(0, i);
+ row.add(1, value);
+ }
+ mCursor = cursor;
+ return 1;
+ }
+}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index a701f8631969..ecd149989ce6 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -139,7 +139,6 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase
final IntSupplier mViewVisibility;
- int mSeq;
int mFrameNumber;
int mFlags;
@@ -156,7 +155,7 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase
void runBenchmark(BenchmarkState state) throws RemoteException {
final IWindowSession session = WindowManagerGlobal.getWindowSession();
while (state.keepRunning()) {
- session.relayout(mWindow, mSeq, mParams, mWidth, mHeight,
+ session.relayout(mWindow, mParams, mWidth, mHeight,
mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrames,
mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, mOutControls,
mOutSurfaceSize, mOutBlastSurfaceControl);
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index c52b1300aedc..b11d74646d67 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -107,7 +107,7 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
final InputChannel inputChannel = new InputChannel();
long startTime = SystemClock.elapsedRealtimeNanos();
- session.addToDisplay(this, mSeq, mLayoutParams, View.VISIBLE,
+ session.addToDisplay(this, mLayoutParams, View.VISIBLE,
Display.DEFAULT_DISPLAY, mOutFrame, mOutContentInsets, mOutStableInsets,
mOutDisplayCutout, inputChannel, mOutInsetsState, mOutControls);
final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index bb9f13f1712c..5cebf8d91cfc 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -128,7 +128,7 @@ class BlobStoreConfig {
*/
public static final String KEY_USE_REVOCABLE_FD_FOR_READS =
"use_revocable_fd_for_reads";
- public static final boolean DEFAULT_USE_REVOCABLE_FD_FOR_READS = true;
+ public static final boolean DEFAULT_USE_REVOCABLE_FD_FOR_READS = false;
public static boolean USE_REVOCABLE_FD_FOR_READS =
DEFAULT_USE_REVOCABLE_FD_FOR_READS;
diff --git a/apex/media/OWNERS b/apex/media/OWNERS
index e83ea3a5087a..ced2fb5e2dcd 100644
--- a/apex/media/OWNERS
+++ b/apex/media/OWNERS
@@ -1,7 +1,10 @@
andrewlewis@google.com
aquilescanta@google.com
chz@google.com
+hdmoon@google.com
hkuang@google.com
+jinpark@google.com
+klhyun@google.com
lnilsson@google.com
marcone@google.com
sungsoo@google.com
diff --git a/apex/media/aidl/Android.bp b/apex/media/aidl/Android.bp
index 409a04897f56..c2b00d5849c4 100644
--- a/apex/media/aidl/Android.bp
+++ b/apex/media/aidl/Android.bp
@@ -15,21 +15,21 @@
//
filegroup {
- name: "stable-mediasession2-aidl-srcs",
+ name: "stable-media-aidl-srcs",
srcs: ["stable/**/*.aidl"],
path: "stable",
}
filegroup {
- name: "private-mediasession2-aidl-srcs",
+ name: "private-media-aidl-srcs",
srcs: ["private/**/I*.aidl"],
path: "private",
}
filegroup {
- name: "mediasession2-aidl-srcs",
+ name: "media-aidl-srcs",
srcs: [
- ":private-mediasession2-aidl-srcs",
- ":stable-mediasession2-aidl-srcs",
+ ":private-media-aidl-srcs",
+ ":stable-media-aidl-srcs",
],
}
diff --git a/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl b/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl
new file mode 100644
index 000000000000..92d673fd25cb
--- /dev/null
+++ b/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+parcelable MediaParceledListSlice<T>;
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index ce4b030467a7..813631e28a88 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -54,9 +54,10 @@ java_library {
filegroup {
name: "updatable-media-srcs",
srcs: [
+ ":media-aidl-srcs",
+ ":mediaparceledlistslice-java-srcs",
":mediaparser-srcs",
":mediasession2-java-srcs",
- ":mediasession2-aidl-srcs",
],
}
@@ -77,6 +78,15 @@ filegroup {
}
filegroup {
+ name: "mediaparceledlistslice-java-srcs",
+ srcs: [
+ "java/android/media/MediaParceledListSlice.java",
+ "java/android/media/BaseMediaParceledListSlice.java",
+ ],
+ path: "java",
+}
+
+filegroup {
name: "mediaparser-srcs",
srcs: [
"java/android/media/MediaParser.java"
diff --git a/apex/media/framework/api/module-lib-current.txt b/apex/media/framework/api/module-lib-current.txt
index d802177e249b..d2826d01664c 100644
--- a/apex/media/framework/api/module-lib-current.txt
+++ b/apex/media/framework/api/module-lib-current.txt
@@ -1 +1,15 @@
// Signature format: 2.0
+package android.media {
+
+ public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable {
+ ctor public MediaParceledListSlice(@NonNull java.util.List<T>);
+ method public int describeContents();
+ method @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList();
+ method public java.util.List<T> getList();
+ method public void setInlineCountLimit(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR;
+ }
+
+}
+
diff --git a/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java b/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java
new file mode 100644
index 000000000000..fb666098301a
--- /dev/null
+++ b/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is a copied version of BaseParceledListSlice in framework with hidden API usages
+ * removed.
+ *
+ * Transfer a large list of Parcelable objects across an IPC. Splits into
+ * multiple transactions if needed.
+ *
+ * Caveat: for efficiency and security, all elements must be the same concrete type.
+ * In order to avoid writing the class name of each object, we must ensure that
+ * each object is the same type, or else unparceling then reparceling the data may yield
+ * a different result if the class name encoded in the Parcelable is a Base type.
+ * See b/17671747.
+ *
+ * @hide
+ */
+abstract class BaseMediaParceledListSlice<T> implements Parcelable {
+ private static String TAG = "BaseMediaParceledListSlice";
+ private static boolean DEBUG = false;
+
+ /*
+ * TODO get this number from somewhere else. For now set it to a quarter of
+ * the 1MB limit.
+ */
+ // private static final int MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes();
+ private static final int MAX_IPC_SIZE = 64 * 1024;
+
+ private final List<T> mList;
+
+ private int mInlineCountLimit = Integer.MAX_VALUE;
+
+ public BaseMediaParceledListSlice(List<T> list) {
+ mList = list;
+ }
+
+ @SuppressWarnings("unchecked")
+ BaseMediaParceledListSlice(Parcel p, ClassLoader loader) {
+ final int N = p.readInt();
+ mList = new ArrayList<T>(N);
+ if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
+ if (N <= 0) {
+ return;
+ }
+
+ Parcelable.Creator<?> creator = readParcelableCreator(p, loader);
+ Class<?> listElementClass = null;
+
+ int i = 0;
+ while (i < N) {
+ if (p.readInt() == 0) {
+ break;
+ }
+
+ final T parcelable = readCreator(creator, p, loader);
+ if (listElementClass == null) {
+ listElementClass = parcelable.getClass();
+ } else {
+ verifySameType(listElementClass, parcelable.getClass());
+ }
+
+ mList.add(parcelable);
+
+ if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1));
+ i++;
+ }
+ if (i >= N) {
+ return;
+ }
+ final IBinder retriever = p.readStrongBinder();
+ while (i < N) {
+ if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever);
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInt(i);
+ try {
+ retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
+ return;
+ }
+ while (i < N && reply.readInt() != 0) {
+ final T parcelable = readCreator(creator, reply, loader);
+ verifySameType(listElementClass, parcelable.getClass());
+
+ mList.add(parcelable);
+
+ if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
+ i++;
+ }
+ reply.recycle();
+ data.recycle();
+ }
+ }
+
+ private T readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader) {
+ if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
+ Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+ (Parcelable.ClassLoaderCreator<?>) creator;
+ return (T) classLoaderCreator.createFromParcel(p, loader);
+ }
+ return (T) creator.createFromParcel(p);
+ }
+
+ private static void verifySameType(final Class<?> expected, final Class<?> actual) {
+ if (!actual.equals(expected)) {
+ throw new IllegalArgumentException("Can't unparcel type "
+ + (actual == null ? null : actual.getName()) + " in list of type "
+ + (expected == null ? null : expected.getName()));
+ }
+ }
+
+ public List<T> getList() {
+ return mList;
+ }
+
+ /**
+ * Set a limit on the maximum number of entries in the array that will be included
+ * inline in the initial parcelling of this object.
+ */
+ public void setInlineCountLimit(int maxCount) {
+ mInlineCountLimit = maxCount;
+ }
+
+ /**
+ * Write this to another Parcel. Note that this discards the internal Parcel
+ * and should not be used anymore. This is so we can pass this to a Binder
+ * where we won't have a chance to call recycle on this.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ final int N = mList.size();
+ final int callFlags = flags;
+ dest.writeInt(N);
+ if (DEBUG) Log.d(TAG, "Writing " + N + " items");
+ if (N > 0) {
+ final Class<?> listElementClass = mList.get(0).getClass();
+ writeParcelableCreator(mList.get(0), dest);
+ int i = 0;
+ while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) {
+ dest.writeInt(1);
+
+ final T parcelable = mList.get(i);
+ verifySameType(listElementClass, parcelable.getClass());
+ writeElement(parcelable, dest, callFlags);
+
+ if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
+ i++;
+ }
+ if (i < N) {
+ dest.writeInt(0);
+ Binder retriever = new Binder() {
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ if (code != FIRST_CALL_TRANSACTION) {
+ return super.onTransact(code, data, reply, flags);
+ }
+ int i = data.readInt();
+ if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
+ while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
+ reply.writeInt(1);
+
+ final T parcelable = mList.get(i);
+ verifySameType(listElementClass, parcelable.getClass());
+ writeElement(parcelable, reply, callFlags);
+
+ if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
+ i++;
+ }
+ if (i < N) {
+ if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
+ reply.writeInt(0);
+ }
+ return true;
+ }
+ };
+ if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever);
+ dest.writeStrongBinder(retriever);
+ }
+ }
+ }
+
+ abstract void writeElement(T parcelable, Parcel reply, int callFlags);
+
+ abstract void writeParcelableCreator(T parcelable, Parcel dest);
+
+ abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader);
+}
diff --git a/apex/media/framework/java/android/media/MediaParceledListSlice.java b/apex/media/framework/java/android/media/MediaParceledListSlice.java
new file mode 100644
index 000000000000..e1223f6a6bed
--- /dev/null
+++ b/apex/media/framework/java/android/media/MediaParceledListSlice.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This is a copied version of MediaParceledListSlice in framework with hidden API usages removed,
+ * and also with some lint error fixed.
+ *
+ * Transfer a large list of Parcelable objects across an IPC. Splits into
+ * multiple transactions if needed.
+ *
+ * @see BaseMediaParceledListSlice
+ *
+ * TODO: Remove this from @SystemApi once all the MediaSession related classes are moved
+ * to apex (or ParceledListSlice moved to apex). This class is temporaily added to system API
+ * for moving classes step by step.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class MediaParceledListSlice<T extends Parcelable>
+ extends BaseMediaParceledListSlice<T> {
+ public MediaParceledListSlice(@NonNull List<T> list) {
+ super(list);
+ }
+
+ private MediaParceledListSlice(Parcel in, ClassLoader loader) {
+ super(in, loader);
+ }
+
+ @NonNull
+ public static <T extends Parcelable> MediaParceledListSlice<T> emptyList() {
+ return new MediaParceledListSlice<T>(Collections.<T> emptyList());
+ }
+
+ @Override
+ public int describeContents() {
+ int contents = 0;
+ final List<T> list = getList();
+ for (int i=0; i<list.size(); i++) {
+ contents |= list.get(i).describeContents();
+ }
+ return contents;
+ }
+
+ @Override
+ void writeElement(T parcelable, Parcel dest, int callFlags) {
+ parcelable.writeToParcel(dest, callFlags);
+ }
+
+ @Override
+ void writeParcelableCreator(T parcelable, Parcel dest) {
+ dest.writeParcelableCreator((Parcelable) parcelable);
+ }
+
+ @Override
+ Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader) {
+ return from.readParcelableCreator(loader);
+ }
+
+ @NonNull
+ @SuppressWarnings("unchecked")
+ public static final Parcelable.ClassLoaderCreator<MediaParceledListSlice> CREATOR =
+ new Parcelable.ClassLoaderCreator<MediaParceledListSlice>() {
+ public MediaParceledListSlice createFromParcel(Parcel in) {
+ return new MediaParceledListSlice(in, null);
+ }
+
+ @Override
+ public MediaParceledListSlice createFromParcel(Parcel in, ClassLoader loader) {
+ return new MediaParceledListSlice(in, loader);
+ }
+
+ @Override
+ public MediaParceledListSlice[] newArray(int size) {
+ return new MediaParceledListSlice[size];
+ }
+ };
+}
diff --git a/api/current.txt b/api/current.txt
index 4b0901a122e7..2c2a5a879417 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6138,6 +6138,7 @@ package android.app {
field @NonNull public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR;
field public static final int FLAG_CANCEL_CURRENT = 268435456; // 0x10000000
field public static final int FLAG_IMMUTABLE = 67108864; // 0x4000000
+ field public static final int FLAG_MUTABLE = 33554432; // 0x2000000
field public static final int FLAG_NO_CREATE = 536870912; // 0x20000000
field public static final int FLAG_ONE_SHOT = 1073741824; // 0x40000000
field public static final int FLAG_UPDATE_CURRENT = 134217728; // 0x8000000
@@ -8783,6 +8784,7 @@ package android.bluetooth {
method public void onPhyUpdate(android.bluetooth.BluetoothGatt, int, int, int);
method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
+ method public void onServiceChanged(@NonNull android.bluetooth.BluetoothGatt);
method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
}
@@ -26358,6 +26360,7 @@ package android.media {
method public boolean containsKey(String);
method public int describeContents();
method public android.graphics.Bitmap getBitmap(String);
+ method @IntRange(from=0) public int getBitmapDimensionLimit();
method @NonNull public android.media.MediaDescription getDescription();
method public long getLong(String);
method public android.media.Rating getRating(String);
@@ -26407,6 +26410,7 @@ package android.media {
method public android.media.MediaMetadata.Builder putRating(String, android.media.Rating);
method public android.media.MediaMetadata.Builder putString(String, String);
method public android.media.MediaMetadata.Builder putText(String, CharSequence);
+ method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(int);
}
@Deprecated public abstract class MediaMetadataEditor {
@@ -46096,6 +46100,7 @@ package android.telecom {
field public static final int MISSED = 5; // 0x5
field public static final int OTHER = 9; // 0x9
field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
+ field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
field public static final int REJECTED = 6; // 0x6
@@ -46795,6 +46800,7 @@ package android.telephony {
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
+ field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool";
@@ -46989,6 +46995,10 @@ package android.telephony {
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
field public static final int SERVICE_CLASS_NONE = 0; // 0x0
field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
+ field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
+ field public static final int USSD_OVER_CS_PREFERRED = 0; // 0x0
+ field public static final int USSD_OVER_IMS_ONLY = 3; // 0x3
+ field public static final int USSD_OVER_IMS_PREFERRED = 1; // 0x1
}
public static final class CarrierConfigManager.Apn {
@@ -53642,6 +53652,33 @@ package android.view {
field public int toolType;
}
+ public interface OnReceiveContentCallback<T extends android.view.View> {
+ method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull T);
+ method public boolean onReceiveContent(@NonNull T, @NonNull android.view.OnReceiveContentCallback.Payload);
+ }
+
+ public static final class OnReceiveContentCallback.Payload {
+ method @NonNull public android.content.ClipData getClip();
+ method @Nullable public android.os.Bundle getExtras();
+ method public int getFlags();
+ method @Nullable public android.net.Uri getLinkUri();
+ method public int getSource();
+ field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+ field public static final int SOURCE_AUTOFILL = 3; // 0x3
+ field public static final int SOURCE_CLIPBOARD = 0; // 0x0
+ field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2
+ field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
+ field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4
+ }
+
+ public static final class OnReceiveContentCallback.Payload.Builder {
+ ctor public OnReceiveContentCallback.Payload.Builder(@NonNull android.content.ClipData, int);
+ method @NonNull public android.view.OnReceiveContentCallback.Payload build();
+ method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setFlags(int);
+ method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setLinkUri(@Nullable android.net.Uri);
+ }
+
public abstract class OrientationEventListener {
ctor public OrientationEventListener(android.content.Context);
ctor public OrientationEventListener(android.content.Context, int);
@@ -54193,6 +54230,7 @@ package android.view {
method @IdRes public int getNextFocusRightId();
method @IdRes public int getNextFocusUpId();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
+ method @Nullable public android.view.OnReceiveContentCallback<? extends android.view.View> getOnReceiveContentCallback();
method @ColorInt public int getOutlineAmbientShadowColor();
method public android.view.ViewOutlineProvider getOutlineProvider();
method @ColorInt public int getOutlineSpotShadowColor();
@@ -54544,6 +54582,7 @@ package android.view {
method public void setOnHoverListener(android.view.View.OnHoverListener);
method public void setOnKeyListener(android.view.View.OnKeyListener);
method public void setOnLongClickListener(@Nullable android.view.View.OnLongClickListener);
+ method public void setOnReceiveContentCallback(@Nullable android.view.OnReceiveContentCallback<? extends android.view.View>);
method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
method @Deprecated public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
@@ -60806,17 +60845,6 @@ package android.widget {
method public android.view.View newGroupView(android.content.Context, android.database.Cursor, boolean, android.view.ViewGroup);
}
- public interface RichContentReceiver<T extends android.view.View> {
- method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes();
- method public boolean onReceive(@NonNull T, @NonNull android.content.ClipData, int, int);
- field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
- field public static final int SOURCE_AUTOFILL = 3; // 0x3
- field public static final int SOURCE_CLIPBOARD = 0; // 0x0
- field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2
- field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
- field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4
- }
-
public class ScrollView extends android.widget.FrameLayout {
ctor public ScrollView(android.content.Context);
ctor public ScrollView(android.content.Context, android.util.AttributeSet);
@@ -61371,10 +61399,10 @@ package android.widget {
method public int getMinWidth();
method public final android.text.method.MovementMethod getMovementMethod();
method public int getOffsetForPosition(float, float);
+ method @Nullable public android.view.OnReceiveContentCallback<android.widget.TextView> getOnReceiveContentCallback();
method public android.text.TextPaint getPaint();
method public int getPaintFlags();
method public String getPrivateImeOptions();
- method @NonNull public android.widget.RichContentReceiver<android.widget.TextView> getRichContentReceiver();
method public int getSelectionEnd();
method public int getSelectionStart();
method @ColorInt public int getShadowColor();
@@ -61502,7 +61530,6 @@ package android.widget {
method public void setPaintFlags(int);
method public void setPrivateImeOptions(String);
method public void setRawInputType(int);
- method public void setRichContentReceiver(@NonNull android.widget.RichContentReceiver<android.widget.TextView>);
method public void setScroller(android.widget.Scroller);
method public void setSelectAllOnFocus(boolean);
method public void setShadowLayer(float, float, float, int);
@@ -61543,7 +61570,6 @@ package android.widget {
method public void setWidth(int);
field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
- field @NonNull public static final android.widget.RichContentReceiver<android.widget.TextView> DEFAULT_RICH_CONTENT_RECEIVER;
}
public enum TextView.BufferType {
@@ -61560,6 +61586,12 @@ package android.widget {
field @NonNull public static final android.os.Parcelable.Creator<android.widget.TextView.SavedState> CREATOR;
}
+ public class TextViewOnReceiveContentCallback implements android.view.OnReceiveContentCallback<android.widget.TextView> {
+ ctor public TextViewOnReceiveContentCallback();
+ method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull android.widget.TextView);
+ method public boolean onReceiveContent(@NonNull android.widget.TextView, @NonNull android.view.OnReceiveContentCallback.Payload);
+ }
+
public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter {
method @Nullable public android.content.res.Resources.Theme getDropDownViewTheme();
method public void setDropDownViewTheme(@Nullable android.content.res.Resources.Theme);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index be8ea9ca6a4d..4a7c1210267e 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -16,6 +16,14 @@ package android.app {
}
+package android.app.role {
+
+ public final class RoleManager {
+ method @Nullable public String getDefaultSmsPackage(int);
+ }
+
+}
+
package android.content.rollback {
public class RollbackManagerFrameworkInitializer {
@@ -45,8 +53,14 @@ package android.media {
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
- public static final class MediaMetadata.Builder {
- ctor public MediaMetadata.Builder(@NonNull android.media.MediaMetadata, @IntRange(from=1) int);
+ public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable {
+ ctor public MediaParceledListSlice(@NonNull java.util.List<T>);
+ method public int describeContents();
+ method @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList();
+ method public java.util.List<T> getList();
+ method public void setInlineCountLimit(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR;
}
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 118184df9d00..949a54a781db 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7429,7 +7429,7 @@ package android.net.wifi {
field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
field public static final String EXTRA_CHANGE_REASON = "changeReason";
field @Deprecated public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
- field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
+ field @Deprecated public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK";
field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
field public static final String EXTRA_URL = "android.net.wifi.extra.URL";
@@ -7437,7 +7437,7 @@ package android.net.wifi {
field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME";
field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE";
field public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
- field public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
+ field @Deprecated public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
field public static final String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et";
field public static final String EXTRA_WIFI_CREDENTIAL_SSID = "ssid";
field public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0; // 0x0
@@ -10441,10 +10441,6 @@ package android.telecom {
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
}
- public final class DisconnectCause implements android.os.Parcelable {
- field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
- }
-
public abstract class InCallService extends android.app.Service {
method @Deprecated public android.telecom.Phone getPhone();
method @Deprecated public void onPhoneCreated(android.telecom.Phone);
@@ -11652,11 +11648,11 @@ package android.telephony.data {
method public int getSuggestedRetryTime();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
- field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 2; // 0x2
- field public static final int HANDOVER_FAILURE_MODE_LEGACY = 1; // 0x1
- field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 3; // 0x3
- field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 4; // 0x4
- field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = 0; // 0x0
+ field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; // 0x1
+ field public static final int HANDOVER_FAILURE_MODE_LEGACY = 0; // 0x0
+ field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 2; // 0x2
+ field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 3; // 0x3
+ field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = -1; // 0xffffffff
field public static final int LINK_STATUS_ACTIVE = 2; // 0x2
field public static final int LINK_STATUS_DORMANT = 1; // 0x1
field public static final int LINK_STATUS_INACTIVE = 0; // 0x0
diff --git a/api/test-current.txt b/api/test-current.txt
index 9383152722be..8e437f14eeb8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -83,6 +83,7 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
method public long getTotalRam();
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
+ method @RequiresPermission("android.permission.INJECT_EVENTS") public void holdLock(int);
method public static boolean isHighEndGfx();
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[], @NonNull String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
@@ -766,6 +767,7 @@ package android.app.role {
method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @Nullable public String getDefaultSmsPackage(int);
method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
@@ -1022,6 +1024,7 @@ package android.content.pm {
method @Nullable public String getSystemTextClassifierPackageName();
method @Nullable public String getWellbeingPackageName();
method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+ method @RequiresPermission("android.permission.INJECT_EVENTS") public void holdLock(int);
method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull String);
@@ -5288,6 +5291,7 @@ package android.view {
}
public interface WindowManager extends android.view.ViewManager {
+ method @RequiresPermission("android.permission.INJECT_EVENTS") public default void holdLock(int);
method public default void setShouldShowIme(int, boolean);
method public default void setShouldShowSystemDecors(int, boolean);
method public default void setShouldShowWithInsecureKeyguard(int, boolean);
@@ -5580,6 +5584,11 @@ package android.widget {
method public void disableClockTick();
}
+ @android.widget.RemoteViews.RemoteView public class TextView extends android.view.View implements android.view.ViewTreeObserver.OnPreDrawListener {
+ method public void onActivityResult(int, int, @Nullable android.content.Intent);
+ field public static final int PROCESS_TEXT_REQUEST_CODE = 100; // 0x64
+ }
+
public class TimePicker extends android.widget.FrameLayout {
method public android.view.View getAmView();
method public android.view.View getHourView();
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 0a09801708a5..c327d1a14257 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -499,7 +499,7 @@ message Atom {
}
// Pulled events will start at field 10000.
- // Next: 10084
+ // Next: 10087
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -598,6 +598,7 @@ message Atom {
DNDModeProto dnd_mode_rule = 10084 [(module) = "framework"];
GeneralExternalStorageAccessStats general_external_storage_access_stats =
10085 [(module) = "mediaprovider"];
+ IncomingSms incoming_sms = 10086 [(module) = "telephony"];
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -3346,6 +3347,7 @@ message StyleUIChanged {
optional int32 wallpaper_id_hash = 8;
optional int32 color_preference = 9;
optional android.stats.style.LocationPreference location_preference = 10;
+ optional android.stats.style.DatePreference date_preference = 11;
}
/**
@@ -10460,6 +10462,59 @@ message SupportedRadioAccessFamily {
}
/**
+ * Pulls information for a single incoming SMS.
+ *
+ * Each pull creates multiple atoms, one for each SMS. The sequence is randomized when pulled.
+ *
+ * Pulled from:
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+ */
+message IncomingSms {
+ // Format of the SMS (3GPP or 3GPP2).
+ optional android.telephony.SmsFormatEnum sms_format = 1;
+
+ // Technology of the SMS (CS or IMS).
+ optional android.telephony.SmsTechEnum sms_tech = 2;
+
+ // Radio access technology (RAT) used for the SMS. It can be IWLAN in case of IMS.
+ optional android.telephony.NetworkTypeEnum rat = 3;
+
+ // Type the SMS.
+ optional android.telephony.SmsTypeEnum sms_type = 4;
+
+ // Number of total parts.
+ optional int32 total_parts = 5;
+
+ // Number of received parts (if smaller than total parts, the SMS was dropped).
+ optional int32 received_parts = 6;
+
+ // Indicates if the incoming SMS was blocked.
+ optional bool blocked = 7;
+
+ // Indicate a specific error handling the SMS
+ optional android.telephony.SmsIncomingErrorEnum error = 8;
+
+ // Whether the SMS was received while roaming.
+ optional bool is_roaming = 9;
+
+ // Index of the SIM is used, 0 for single-SIM devices.
+ optional int32 sim_slot_index = 10;
+
+ // Whether the device was in multi-SIM mode (with multiple active SIM profiles).
+ optional bool is_multi_sim = 11;
+
+ // Whether the message was received with an eSIM profile.
+ optional bool is_esim = 12;
+
+ // Carrier ID of the SIM card used for the SMS.
+ // See https://source.android.com/devices/tech/config/carrierid.
+ optional int32 carrier_id = 13;
+
+ // Random message ID.
+ optional int64 message_id = 14;
+}
+
+/**
* Logs gnss stats from location service provider
*
* Pulled from:
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index 2ae57638791e..12d3b9487635 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -501,6 +501,21 @@ bool determineAllMetricUpdateStatuses(const StatsdConfig& config,
const set<int64_t>& replacedStates,
vector<UpdateStatus>& metricsToUpdate) {
int metricIndex = 0;
+ for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) {
+ const CountMetric& metric = config.count_metric(i);
+ set<int64_t> conditionDependencies;
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_COUNT, {metric.what()},
+ conditionDependencies, metric.slice_by_state(), metric.links(),
+ oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
const EventMetric& metric = config.event_metric(i);
set<int64_t> conditionDependencies;
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index 65e5875e39bf..3b346c11e831 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -1094,6 +1094,162 @@ TEST_F(ConfigUpdateTest, TestEventMetricActivationDepsChange) {
EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
}
+TEST_F(ConfigUpdateTest, TestCountMetricPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->add_slice_by_state(sliceState.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricDefinitionChange) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ // Change bucket size, which should change the proto, causing replacement.
+ metric->set_bucket(TEN_MINUTES);
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricWhatChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricConditionChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricStateChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->add_slice_by_state(sliceState.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
StatsdConfig config;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 70ca49d67930..e75d2f60bb87 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4796,4 +4796,19 @@ public class ActivityManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Holds the AM lock for the specified amount of milliseconds.
+ * This is intended for use by the tests that need to imitate lock contention.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
+ public void holdLock(int durationMs) {
+ try {
+ getService().holdLock(durationMs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 167b5a8029c0..ef4f099f441d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -81,8 +81,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
@@ -5021,8 +5019,7 @@ public class AppOpsManager {
* @hide
*/
public static double round(double value) {
- final BigDecimal decimalScale = new BigDecimal(value);
- return decimalScale.setScale(0, RoundingMode.HALF_UP).doubleValue();
+ return Math.floor(value + 0.5);
}
@Override
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 95bbebecd66e..1a4db4e541cc 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -696,4 +696,10 @@ interface IActivityManager {
* @param enable set it to true to enable the app freezer, false to disable it.
*/
boolean enableAppFreezer(in boolean enable);
+
+ /**
+ * Holds the AM lock for the specified amount of milliseconds.
+ * This is intended for use by the tests that need to imitate lock contention.
+ */
+ void holdLock(in int durationMs);
}
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index 59dc9991c832..55fff8b2f074 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -22,12 +22,10 @@ import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.util.Slog;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -387,12 +385,13 @@ public final class NotificationHistory implements Parcelable {
/**
* Removes all notifications from a conversation and regenerates the string pool
*/
- public boolean removeConversationFromWrite(String packageName, String conversationId) {
+ public boolean removeConversationsFromWrite(String packageName, Set<String> conversationIds) {
boolean removed = false;
for (int i = mNotificationsToWrite.size() - 1; i >= 0; i--) {
HistoricalNotification hn = mNotificationsToWrite.get(i);
if (packageName.equals(hn.getPackage())
- && conversationId.equals(hn.getConversationId())) {
+ && hn.getConversationId() != null
+ && conversationIds.contains(hn.getConversationId())) {
removed = true;
mNotificationsToWrite.remove(i);
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index cd352e141994..e8937a8da911 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -19,12 +19,16 @@ package android.app;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -35,6 +39,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.AndroidException;
import android.util.ArraySet;
+import android.util.Log;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.IResultReceiver;
@@ -102,11 +107,20 @@ import java.lang.annotation.RetentionPolicy;
* FLAG_ONE_SHOT, <b>both</b> FLAG_ONE_SHOT and FLAG_NO_CREATE need to be supplied.
*/
public final class PendingIntent implements Parcelable {
+ private static final String TAG = "PendingIntent";
private final IIntentSender mTarget;
private IResultReceiver mCancelReceiver;
private IBinder mWhitelistToken;
private ArraySet<CancelListener> mCancelListeners;
+ /**
+ * It is now required to specify either {@link #FLAG_IMMUTABLE}
+ * or {@link #FLAG_MUTABLE} when creating a PendingIntent.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R)
+ static final long PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED = 160794467L;
+
/** @hide */
@IntDef(flag = true,
value = {
@@ -115,6 +129,7 @@ public final class PendingIntent implements Parcelable {
FLAG_CANCEL_CURRENT,
FLAG_UPDATE_CURRENT,
FLAG_IMMUTABLE,
+ FLAG_MUTABLE,
Intent.FILL_IN_ACTION,
Intent.FILL_IN_DATA,
@@ -175,6 +190,21 @@ public final class PendingIntent implements Parcelable {
public static final int FLAG_IMMUTABLE = 1<<26;
/**
+ * Flag indicating that the created PendingIntent should be mutable.
+ * This flag cannot be combined with {@link #FLAG_IMMUTABLE}. <p>Up until
+ * {@link android.os.Build.VERSION_CODES#R}, PendingIntents are assumed to
+ * be mutable by default, unless {@link #FLAG_IMMUTABLE} is set. Starting
+ * with {@link android.os.Build.VERSION_CODES#S}, it will be required to
+ * explicitly specify the mutability of PendingIntents on creation with
+ * either (@link #FLAG_IMMUTABLE} or {@link #FLAG_MUTABLE}. It is strongly
+ * recommended to use {@link #FLAG_IMMUTABLE} when creating a
+ * PendingIntent. {@link #FLAG_MUTABLE} should only be used when some
+ * functionality relies on modifying the underlying intent, e.g. any
+ * PendingIntent that needs to be used with inline reply or bubbles.
+ */
+ public static final int FLAG_MUTABLE = 1<<25;
+
+ /**
* Exception thrown when trying to send through a PendingIntent that
* has been canceled or is otherwise no longer able to execute the request.
*/
@@ -286,6 +316,27 @@ public final class PendingIntent implements Parcelable {
sOnMarshaledListener.set(listener);
}
+ private static void checkFlags(int flags, String packageName) {
+ final boolean flagImmutableSet = (flags & PendingIntent.FLAG_IMMUTABLE) != 0;
+ final boolean flagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0;
+ String msg = packageName + ": Targeting S+ (version " + Build.VERSION_CODES.S
+ + " and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE"
+ + " be specified when creating a PendingIntent.\nStrongly consider"
+ + " using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality"
+ + " depends on the PendingIntent being mutable, e.g. if it needs to"
+ + " be used with inline replies or bubbles.";
+
+ if (flagImmutableSet && flagMutableSet) {
+ throw new IllegalArgumentException(
+ "Cannot set both FLAG_IMMUTABLE and FLAG_MUTABLE for PendingIntent");
+ }
+
+ if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED)
+ && !flagImmutableSet && !flagMutableSet) {
+ Log.e(TAG, msg);
+ }
+ }
+
/**
* Retrieve a PendingIntent that will start a new activity, like calling
* {@link Context#startActivity(Intent) Context.startActivity(Intent)}.
@@ -350,6 +401,7 @@ public final class PendingIntent implements Parcelable {
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
+ checkFlags(flags, packageName);
try {
intent.migrateExtraStreamToClipData(context);
intent.prepareToLeaveProcess(context);
@@ -376,6 +428,7 @@ public final class PendingIntent implements Parcelable {
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
+ checkFlags(flags, packageName);
try {
intent.migrateExtraStreamToClipData(context);
intent.prepareToLeaveProcess(context);
@@ -495,6 +548,7 @@ public final class PendingIntent implements Parcelable {
intents[i].prepareToLeaveProcess(context);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
}
+ checkFlags(flags, packageName);
try {
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
@@ -521,6 +575,7 @@ public final class PendingIntent implements Parcelable {
intents[i].prepareToLeaveProcess(context);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
}
+ checkFlags(flags, packageName);
try {
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
@@ -572,6 +627,7 @@ public final class PendingIntent implements Parcelable {
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
+ checkFlags(flags, packageName);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
@@ -651,6 +707,7 @@ public final class PendingIntent implements Parcelable {
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
+ checkFlags(flags, packageName);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl
index 61dac0d64422..c547ef1e7e9b 100644
--- a/core/java/android/app/people/IPeopleManager.aidl
+++ b/core/java/android/app/people/IPeopleManager.aidl
@@ -39,4 +39,10 @@ interface IPeopleManager {
/** Removes all the recent conversations and uncaches their cached shortcuts. */
void removeAllRecentConversations();
+
+ /**
+ * Returns the last interaction with the specified conversation. If the
+ * conversation can't be found or no interactions have been recorded, returns 0L.
+ */
+ long getLastInteraction(in String packageName, int userId, in String shortcutId);
}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 253c73796caf..26edba36fdba 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -634,6 +634,8 @@ public final class RoleManager {
* @hide
*/
@Nullable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
public String getDefaultSmsPackage(@UserIdInt int userId) {
try {
return mService.getDefaultSmsPackage(userId);
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index c58b5d218e74..6d22eb93fd02 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -688,6 +688,31 @@ public final class BluetoothGatt implements BluetoothProfile {
}
});
}
+
+ /**
+ * Callback invoked when service changed event is received
+ * @hide
+ */
+ @Override
+ public void onServiceChanged(String address) {
+ if (DBG) {
+ Log.d(TAG, "onServiceChanged() - Device=" + address);
+ }
+
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+
+ runOrQueueCallback(new Runnable() {
+ @Override
+ public void run() {
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onServiceChanged(BluetoothGatt.this);
+ }
+ }
+ });
+ }
};
/*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index f718c0b57c1b..1c40cff076f6 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.annotation.NonNull;
+
/**
* This abstract class is used to implement {@link BluetoothGatt} callbacks.
*/
@@ -194,4 +196,16 @@ public abstract class BluetoothGattCallback {
public void onConnectionUpdated(BluetoothGatt gatt, int interval, int latency, int timeout,
int status) {
}
+
+ /**
+ * Callback indicating service changed event is received
+ *
+ * <p>Receiving this event means that the GATT database is out of sync with
+ * the remote device. {@link BluetoothGatt#discoverServices} should be
+ * called to re-discover the services.
+ *
+ * @param gatt GATT client involved
+ */
+ public void onServiceChanged(@NonNull BluetoothGatt gatt) {
+ }
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 24282365a8c7..ba894ae72017 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -795,4 +795,6 @@ interface IPackageManager {
boolean isAutoRevokeWhitelisted(String packageName);
void grantImplicitAccess(int queryingUid, String visibleAuthority);
+
+ void holdLock(in int durationMs);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4eec56c0d87b..79e23b37be05 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8305,4 +8305,19 @@ public abstract class PackageManager {
public static void uncorkPackageInfoCache() {
PropertyInvalidatedCache.uncorkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
}
+
+ /**
+ * Holds the PM lock for the specified amount of milliseconds.
+ * Intended for use by the tests that need to imitate lock contention.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
+ public void holdLock(int durationMs) {
+ try {
+ ActivityThread.getPackageManager().holdLock(durationMs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index cc4c45699bd4..a6e8c1395701 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -1245,6 +1245,7 @@ public final class Sensor {
return true;
case TYPE_HINGE_ANGLE:
mStringType = STRING_TYPE_HINGE_ANGLE;
+ return true;
default:
return false;
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 1061121b4449..0d0bfb32e293 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2175,10 +2175,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
/**
* <p>The desired zoom ratio</p>
- * <p>Instead of using {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} with dual purposes of crop and zoom, the
- * application can now choose to use this tag to specify the desired zoom level. The
- * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} can still be used to specify the horizontal or vertical
- * crop to achieve aspect ratios different than the native camera sensor.</p>
+ * <p>Instead of using {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} for zoom, the application can now choose to
+ * use this tag to specify the desired zoom level.</p>
* <p>By using this control, the application gains a simpler way to control zoom, which can
* be a combination of optical and digital zoom. For example, a multi-camera system may
* contain more than one lens with different focal lengths, and the user can use optical
@@ -2860,11 +2858,18 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* respectively.</p>
* <p>The camera device may adjust the crop region to account for rounding and other hardware
* requirements; the final crop region used will be included in the output capture result.</p>
+ * <p>The camera sensor output aspect ratio depends on factors such as output stream
+ * combination and {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}, and shouldn't be adjusted by using
+ * this control. And the camera device will treat different camera sensor output sizes
+ * (potentially with in-sensor crop) as the same crop of
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}. As a result, the application shouldn't assume the
+ * maximum crop region always maps to the same aspect ratio or field of view for the
+ * sensor output.</p>
* <p>Starting from API level 30, it's strongly recommended to use {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}
* to take advantage of better support for zoom with logical multi-camera. The benefits
* include better precision with optical-digital zoom combination, and ability to do
* zoom-out from 1.0x. When using {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for zoom, the crop region in
- * the capture request must be either letterboxing or pillarboxing (but not both). The
+ * the capture request should be left as the default activeArray size. The
* coordinate system is post-zoom, meaning that the activeArraySize or
* preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See
* {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
@@ -2874,6 +2879,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* capability and mode</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
* @see CaptureRequest#CONTROL_ZOOM_RATIO
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 29a53fb6d4a2..8cfa0866f13a 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2405,10 +2405,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
/**
* <p>The desired zoom ratio</p>
- * <p>Instead of using {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} with dual purposes of crop and zoom, the
- * application can now choose to use this tag to specify the desired zoom level. The
- * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} can still be used to specify the horizontal or vertical
- * crop to achieve aspect ratios different than the native camera sensor.</p>
+ * <p>Instead of using {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} for zoom, the application can now choose to
+ * use this tag to specify the desired zoom level.</p>
* <p>By using this control, the application gains a simpler way to control zoom, which can
* be a combination of optical and digital zoom. For example, a multi-camera system may
* contain more than one lens with different focal lengths, and the user can use optical
@@ -3506,11 +3504,18 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* respectively.</p>
* <p>The camera device may adjust the crop region to account for rounding and other hardware
* requirements; the final crop region used will be included in the output capture result.</p>
+ * <p>The camera sensor output aspect ratio depends on factors such as output stream
+ * combination and {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}, and shouldn't be adjusted by using
+ * this control. And the camera device will treat different camera sensor output sizes
+ * (potentially with in-sensor crop) as the same crop of
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}. As a result, the application shouldn't assume the
+ * maximum crop region always maps to the same aspect ratio or field of view for the
+ * sensor output.</p>
* <p>Starting from API level 30, it's strongly recommended to use {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}
* to take advantage of better support for zoom with logical multi-camera. The benefits
* include better precision with optical-digital zoom combination, and ability to do
* zoom-out from 1.0x. When using {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for zoom, the crop region in
- * the capture request must be either letterboxing or pillarboxing (but not both). The
+ * the capture request should be left as the default activeArray size. The
* coordinate system is post-zoom, meaning that the activeArraySize or
* preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See
* {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
@@ -3520,6 +3525,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* capability and mode</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
* @see CaptureRequest#CONTROL_ZOOM_RATIO
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
diff --git a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
index 27f8a61b8999..7b6a457411f3 100644
--- a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
+++ b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
@@ -37,12 +37,16 @@ public class FrameNumberTracker {
/** the completed frame number for each type of capture results */
private long[] mCompletedFrameNumber = new long[CaptureRequest.REQUEST_TYPE_COUNT];
- /** the skipped frame numbers that don't belong to each type of capture results */
- private final LinkedList<Long>[] mSkippedOtherFrameNumbers =
+ /** the frame numbers that don't belong to each type of capture results and are yet to be seen
+ * through an updateTracker() call. Each list holds a list of frame numbers that should appear
+ * with request types other than that, to which the list corresponds.
+ */
+ private final LinkedList<Long>[] mPendingFrameNumbersWithOtherType =
new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT];
- /** the skipped frame numbers that belong to each type of capture results */
- private final LinkedList<Long>[] mSkippedFrameNumbers =
+ /** the frame numbers that belong to each type of capture results which should appear, but
+ * haven't yet.*/
+ private final LinkedList<Long>[] mPendingFrameNumbers =
new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT];
/** frame number -> request type */
@@ -53,8 +57,8 @@ public class FrameNumberTracker {
public FrameNumberTracker() {
for (int i = 0; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) {
mCompletedFrameNumber[i] = CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED;
- mSkippedOtherFrameNumbers[i] = new LinkedList<Long>();
- mSkippedFrameNumbers[i] = new LinkedList<Long>();
+ mPendingFrameNumbersWithOtherType[i] = new LinkedList<Long>();
+ mPendingFrameNumbers[i] = new LinkedList<Long>();
}
}
@@ -66,29 +70,29 @@ public class FrameNumberTracker {
int requestType = (int) pair.getValue();
Boolean removeError = false;
if (errorFrameNumber == mCompletedFrameNumber[requestType] + 1) {
- mCompletedFrameNumber[requestType] = errorFrameNumber;
removeError = true;
+ }
+ // The error frame number could have also either been in the pending list or one of the
+ // 'other' pending lists.
+ if (!mPendingFrameNumbers[requestType].isEmpty()) {
+ if (errorFrameNumber == mPendingFrameNumbers[requestType].element()) {
+ mPendingFrameNumbers[requestType].remove();
+ removeError = true;
+ }
} else {
- if (!mSkippedFrameNumbers[requestType].isEmpty()) {
- if (errorFrameNumber == mSkippedFrameNumbers[requestType].element()) {
- mCompletedFrameNumber[requestType] = errorFrameNumber;
- mSkippedFrameNumbers[requestType].remove();
+ for (int i = 1; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) {
+ int otherType = (requestType + i) % CaptureRequest.REQUEST_TYPE_COUNT;
+ if (!mPendingFrameNumbersWithOtherType[otherType].isEmpty() && errorFrameNumber
+ == mPendingFrameNumbersWithOtherType[otherType].element()) {
+ mPendingFrameNumbersWithOtherType[otherType].remove();
removeError = true;
- }
- } else {
- for (int i = 1; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) {
- int otherType = (requestType + i) % CaptureRequest.REQUEST_TYPE_COUNT;
- if (!mSkippedOtherFrameNumbers[otherType].isEmpty() && errorFrameNumber
- == mSkippedOtherFrameNumbers[otherType].element()) {
- mCompletedFrameNumber[requestType] = errorFrameNumber;
- mSkippedOtherFrameNumbers[otherType].remove();
- removeError = true;
- break;
- }
+ break;
}
}
}
if (removeError) {
+ mCompletedFrameNumber[requestType] = errorFrameNumber;
+ mPartialResults.remove(errorFrameNumber);
iter.remove();
}
}
@@ -182,7 +186,7 @@ public class FrameNumberTracker {
* It validates that all previous frames of the same category have arrived.
*
* If there is a gap since previous frame number of the same category, assume the frames in
- * the gap are other categories and store them in the skipped frame number queue to check
+ * the gap are other categories and store them in the pending frame number queue to check
* against when frames of those categories arrive.
*/
private void updateCompletedFrameNumber(long frameNumber,
@@ -199,25 +203,29 @@ public class FrameNumberTracker {
if (frameNumber < maxOtherFrameNumberSeen) {
// if frame number is smaller than completed frame numbers of other categories,
// it must be:
- // - the head of mSkippedFrameNumbers for this category, or
- // - in one of other mSkippedOtherFrameNumbers
- if (!mSkippedFrameNumbers[requestType].isEmpty()) {
- // frame number must be head of current type of mSkippedFrameNumbers if
- // mSkippedFrameNumbers isn't empty.
- if (frameNumber < mSkippedFrameNumbers[requestType].element()) {
+ // - the head of mPendingFrameNumbers for this category, or
+ // - in one of other mPendingFrameNumbersWithOtherType
+ if (!mPendingFrameNumbers[requestType].isEmpty()) {
+ // frame number must be head of current type of mPendingFrameNumbers if
+ // mPendingFrameNumbers isn't empty.
+ Long pendingFrameNumberSameType = mPendingFrameNumbers[requestType].element();
+ if (frameNumber == pendingFrameNumberSameType) {
+ // frame number matches the head of the pending frame number queue.
+ // Do this before the inequality checks since this is likely to be the common
+ // case.
+ mPendingFrameNumbers[requestType].remove();
+ } else if (frameNumber < pendingFrameNumberSameType) {
throw new IllegalArgumentException("frame number " + frameNumber
+ " is a repeat");
- } else if (frameNumber > mSkippedFrameNumbers[requestType].element()) {
+ } else {
throw new IllegalArgumentException("frame number " + frameNumber
+ " comes out of order. Expecting "
- + mSkippedFrameNumbers[requestType].element());
+ + pendingFrameNumberSameType);
}
- // frame number matches the head of the skipped frame number queue.
- mSkippedFrameNumbers[requestType].remove();
} else {
- // frame number must be in one of the other mSkippedOtherFrameNumbers.
- int index1 = mSkippedOtherFrameNumbers[otherType1].indexOf(frameNumber);
- int index2 = mSkippedOtherFrameNumbers[otherType2].indexOf(frameNumber);
+ // frame number must be in one of the other mPendingFrameNumbersWithOtherType.
+ int index1 = mPendingFrameNumbersWithOtherType[otherType1].indexOf(frameNumber);
+ int index2 = mPendingFrameNumbersWithOtherType[otherType2].indexOf(frameNumber);
boolean inSkippedOther1 = index1 != -1;
boolean inSkippedOther2 = index2 != -1;
if (!(inSkippedOther1 ^ inSkippedOther2)) {
@@ -225,33 +233,39 @@ public class FrameNumberTracker {
+ " is a repeat or invalid");
}
- // We know the category of frame numbers in skippedOtherFrameNumbers leading up
- // to the current frame number. Move them into the correct skippedFrameNumbers.
+ // We know the category of frame numbers in pendingFrameNumbersWithOtherType leading
+ // up to the current frame number. The destination is the type which isn't the
+ // requestType* and isn't the src. Move them into the correct pendingFrameNumbers.
+ // * : This is since frameNumber is the first frame of requestType that we've
+ // received in the 'others' list, since for each request type frames come in order.
+ // All the frames before frameNumber are of the same type. They're not of
+ // 'requestType', neither of the type of the 'others' list they were found in. The
+ // remaining option is the 3rd type.
LinkedList<Long> srcList, dstList;
int index;
if (inSkippedOther1) {
- srcList = mSkippedOtherFrameNumbers[otherType1];
- dstList = mSkippedFrameNumbers[otherType2];
+ srcList = mPendingFrameNumbersWithOtherType[otherType1];
+ dstList = mPendingFrameNumbers[otherType2];
index = index1;
} else {
- srcList = mSkippedOtherFrameNumbers[otherType2];
- dstList = mSkippedFrameNumbers[otherType1];
+ srcList = mPendingFrameNumbersWithOtherType[otherType2];
+ dstList = mPendingFrameNumbers[otherType1];
index = index2;
}
for (int i = 0; i < index; i++) {
dstList.add(srcList.removeFirst());
}
- // Remove current frame number from skippedOtherFrameNumbers
+ // Remove current frame number from pendingFrameNumbersWithOtherType
srcList.remove();
}
} else {
// there is a gap of unseen frame numbers which should belong to the other
- // 2 categories. Put all the skipped frame numbers in the queue.
+ // 2 categories. Put all the pending frame numbers in the queue.
for (long i =
Math.max(maxOtherFrameNumberSeen, mCompletedFrameNumber[requestType]) + 1;
i < frameNumber; i++) {
- mSkippedOtherFrameNumbers[requestType].add(i);
+ mPendingFrameNumbersWithOtherType[requestType].add(i);
}
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index c12bb39c3175..997efbedb0de 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -746,7 +746,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+ public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
if (mService == null) {
Slog.w(TAG, "setUdfpsOverlayController: no fingerprint service");
return;
@@ -763,14 +763,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void onFingerDown(int x, int y, float minor, float major) {
+ public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
if (mService == null) {
Slog.w(TAG, "onFingerDown: no fingerprint service");
return;
}
try {
- mService.onFingerDown(x, y, minor, major);
+ mService.onFingerDown(sensorId, x, y, minor, major);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -780,14 +780,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void onFingerUp() {
+ public void onFingerUp(int sensorId) {
if (mService == null) {
Slog.w(TAG, "onFingerDown: no fingerprint service");
return;
}
try {
- mService.onFingerUp();
+ mService.onFingerUp(sensorId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
index 718141a4845a..d26346c21427 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
@@ -18,9 +18,7 @@ package android.hardware.fingerprint;
import android.annotation.IntDef;
import android.hardware.biometrics.SensorProperties;
-import android.hardware.face.FaceSensorProperties;
import android.os.Parcel;
-import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index cc2b520b3152..68013eae956b 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -121,10 +121,10 @@ interface IFingerprintService {
void initializeConfiguration(int sensorId, int strength);
// Notifies about a finger touching the sensor area.
- void onFingerDown(int x, int y, float minor, float major);
+ void onFingerDown(int sensorId, int x, int y, float minor, float major);
// Notifies about a finger leaving the sensor area.
- void onFingerUp();
+ void onFingerUp(int sensorId);
// Sets the controller for managing the UDFPS overlay.
void setUdfpsOverlayController(in IUdfpsOverlayController controller);
diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
index a57726c4afe4..58b7046ad991 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
@@ -21,11 +21,11 @@ package android.hardware.fingerprint;
*/
oneway interface IUdfpsOverlayController {
// Shows the overlay.
- void showUdfpsOverlay();
+ void showUdfpsOverlay(int sensorId);
// Hides the overlay.
- void hideUdfpsOverlay();
+ void hideUdfpsOverlay(int sensorId);
// Shows debug messages on the UDFPS overlay.
- void setDebugMessage(String message);
+ void setDebugMessage(int sensorId, String message);
}
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index f6cd726dda4a..1173c311bd26 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -78,4 +78,26 @@ public abstract class InputManagerInternal {
*/
public abstract boolean transferTouchFocus(@NonNull IBinder fromChannelToken,
@NonNull IBinder toChannelToken);
+
+ /** Registers the {@link LidSwitchCallback} to begin receiving notifications. */
+ public abstract void registerLidSwitchCallback(@NonNull LidSwitchCallback callbacks);
+
+ /**
+ * Unregisters a {@link LidSwitchCallback callback} previously registered with
+ * {@link #registerLidSwitchCallback(LidSwitchCallback)}.
+ */
+ public abstract void unregisterLidSwitchCallback(@NonNull LidSwitchCallback callbacks);
+
+ /** Callback interface for notifications relating to the lid switch. */
+ public interface LidSwitchCallback {
+ /**
+ * This callback is invoked when the lid switch changes state. Will be triggered once on
+ * registration of the callback with a {@code whenNanos} of 0 and then on every subsequent
+ * change in lid switch state.
+ *
+ * @param whenNanos the time when the change occurred
+ * @param lidOpen true if the lid is open
+ */
+ void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
+ }
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 651494d1c99c..cad103e9f082 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -21,13 +21,14 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.net.util.LinkPropertiesUtils;
-import android.net.util.LinkPropertiesUtils.CompareResult;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.net.module.util.LinkPropertiesUtils;
+import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 0eb3c1e8ad01..51c5a50dcafb 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -20,12 +20,12 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
-import android.net.util.MacAddressUtils;
import android.net.wifi.WifiInfo;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.Preconditions;
+import com.android.net.module.util.MacAddressUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 98760761736d..a8b45e9d128b 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -22,11 +22,12 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.net.util.NetUtils;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.net.module.util.NetUtils;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.Inet4Address;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fdbf79a9f5d2..d889b155cf64 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -21,6 +21,7 @@ import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.job.JobParameters;
import android.compat.annotation.UnsupportedAppUsage;
@@ -2889,14 +2890,17 @@ public abstract class BatteryStats implements Parcelable {
/**
* Returns the approximate CPU time (in microseconds) spent by the system server handling
- * incoming service calls from apps.
+ * incoming service calls from apps. The result is returned as an array of longs,
+ * organized as a sequence like this:
+ * <pre>
+ * cluster1-speeed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...
+ * </pre>
*
- * @param cluster the index of the CPU cluster.
- * @param step the index of the CPU speed. This is not the actual speed of the CPU.
* @see com.android.internal.os.PowerProfile#getNumCpuClusters()
* @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int)
*/
- public abstract long getSystemServiceTimeAtCpuSpeed(int cluster, int step);
+ @Nullable
+ public abstract long[] getSystemServiceTimeAtCpuSpeeds();
/**
* Returns the total, last, or current battery uptime in microseconds.
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index df1f1b21eba3..54f80c613f0a 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -29,6 +29,7 @@ import android.content.pm.ResolveInfo;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
@@ -43,11 +44,14 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-/** @hide */
+/**
+ * GraphicsEnvironment sets up necessary properties for the graphics environment of the
+ * application process.
+ *
+ * @hide
+ */
public class GraphicsEnvironment {
private static final GraphicsEnvironment sInstance = new GraphicsEnvironment();
@@ -64,27 +68,35 @@ public class GraphicsEnvironment {
private static final String SYSTEM_DRIVER_NAME = "system";
private static final String SYSTEM_DRIVER_VERSION_NAME = "";
private static final long SYSTEM_DRIVER_VERSION_CODE = 0;
+
+ // System properties related to updatable graphics drivers.
private static final String PROPERTY_GFX_DRIVER_PRODUCTION = "ro.gfx.driver.0";
private static final String PROPERTY_GFX_DRIVER_PRERELEASE = "ro.gfx.driver.1";
private static final String PROPERTY_GFX_DRIVER_BUILD_TIME = "ro.gfx.driver_build_time";
+
+ // Metadata flags within the <application> tag in the AndroidManifest.xml file.
private static final String METADATA_DRIVER_BUILD_TIME =
"com.android.graphics.driver.build_time";
private static final String METADATA_DEVELOPER_DRIVER_ENABLE =
"com.android.graphics.developerdriver.enable";
private static final String METADATA_INJECT_LAYERS_ENABLE =
"com.android.graphics.injectLayers.enable";
+
+ private static final String UPDATABLE_DRIVER_ALLOWLIST_ALL = "*";
+ private static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
+
+ // ANGLE related properties.
private static final String ANGLE_RULES_FILE = "a4a_rules.json";
private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
private static final String ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE =
"android.app.action.ANGLE_FOR_ANDROID_TOAST_MESSAGE";
private static final String INTENT_KEY_A4A_TOAST_MESSAGE = "A4A Toast Message";
- private static final String UPDATABLE_DRIVER_ALLOWLIST_ALL = "*";
- private static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
+
private static final int VULKAN_1_0 = 0x00400000;
private static final int VULKAN_1_1 = 0x00401000;
- // UPDATABLE_DRIVER_ALL_APPS
+ // Values for UPDATABLE_DRIVER_ALL_APPS
// 0: Default (Invalid values fallback to default as well)
// 1: All apps use updatable production driver
// 2: All apps use updatable prerelease driver
@@ -94,6 +106,15 @@ public class GraphicsEnvironment {
private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER = 2;
private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF = 3;
+ // Values for GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE
+ private static final int ANGLE_GL_DRIVER_ALL_ANGLE_ON = 1;
+ private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0;
+
+ // Values for GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES
+ private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default";
+ private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle";
+ private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
+
private ClassLoader mClassLoader;
private String mLibrarySearchPaths;
private String mLibraryPermittedPaths;
@@ -106,12 +127,15 @@ public class GraphicsEnvironment {
final String packageName = context.getPackageName();
final ApplicationInfo appInfoWithMetaData =
getAppInfoWithMetadata(context, pm, packageName);
+
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers");
setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData);
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupAngle");
setupAngle(context, coreSettings, pm, packageName);
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "chooseDriver");
if (!chooseDriver(context, coreSettings, pm, packageName, appInfoWithMetaData)) {
setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE,
@@ -132,36 +156,30 @@ public class GraphicsEnvironment {
*/
public static boolean shouldUseAngle(Context context, Bundle coreSettings,
String packageName) {
- if (packageName.isEmpty()) {
- Log.v(TAG, "No package name available yet, ANGLE should not be used");
+ if (TextUtils.isEmpty(packageName)) {
+ Log.v(TAG, "No package name specified, ANGLE should not be used");
return false;
}
- final String devOptIn = getDriverForPkg(context, coreSettings, packageName);
- if (DEBUG) {
- Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
- + "set to: '" + devOptIn + "'");
- }
+ final String devOptIn = getDriverForPackage(context, coreSettings, packageName);
+ Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
+ + "set to: '" + devOptIn + "'");
- // We only want to use ANGLE if the app is allowlisted or the developer has
+ // We only want to use ANGLE if the app is in the allowlist, or the developer has
// explicitly chosen something other than default driver.
// The allowlist will be generated by the ANGLE APK at both boot time and
// ANGLE update time. It will only include apps mentioned in the rules file.
- final boolean allowlisted = checkAngleAllowlist(context, coreSettings, packageName);
- final boolean requested = devOptIn.equals(sDriverMap.get(OpenGlDriverChoice.ANGLE));
- final boolean useAngle = (allowlisted || requested);
- if (!useAngle) {
- return false;
- }
+ final boolean allowed = checkAngleAllowlist(context, coreSettings, packageName);
+ final boolean requested = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
- if (allowlisted) {
+ if (allowed) {
Log.v(TAG, "ANGLE allowlist includes " + packageName);
}
if (requested) {
Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn);
}
- return true;
+ return allowed || requested;
}
private static int getVulkanVersion(PackageManager pm) {
@@ -249,6 +267,7 @@ public class GraphicsEnvironment {
}
if (appInfo == null) {
Log.w(TAG, "Debug layer app '" + packageName + "' not installed");
+ return "";
}
final String abi = chooseAbi(appInfo);
@@ -315,23 +334,6 @@ public class GraphicsEnvironment {
setLayerPaths(mClassLoader, layerPaths);
}
- enum OpenGlDriverChoice {
- DEFAULT,
- NATIVE,
- ANGLE
- }
-
- private static final Map<OpenGlDriverChoice, String> sDriverMap = buildMap();
- private static Map<OpenGlDriverChoice, String> buildMap() {
- final Map<OpenGlDriverChoice, String> map = new HashMap<>();
- map.put(OpenGlDriverChoice.DEFAULT, "default");
- map.put(OpenGlDriverChoice.ANGLE, "angle");
- map.put(OpenGlDriverChoice.NATIVE, "native");
-
- return map;
- }
-
-
private static List<String> getGlobalSettingsString(ContentResolver contentResolver,
Bundle bundle,
String globalSetting) {
@@ -378,18 +380,25 @@ public class GraphicsEnvironment {
return ai;
}
- private static String getDriverForPkg(Context context, Bundle bundle, String packageName) {
- final String allUseAngle;
+ private static String getDriverForPackage(Context context, Bundle bundle, String packageName) {
+ final int allUseAngle;
if (bundle != null) {
allUseAngle =
- bundle.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
+ bundle.getInt(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
} else {
ContentResolver contentResolver = context.getContentResolver();
- allUseAngle = Settings.Global.getString(contentResolver,
- Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
+ allUseAngle = Settings.Global.getInt(contentResolver,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
+ ANGLE_GL_DRIVER_ALL_ANGLE_OFF);
}
- if ((allUseAngle != null) && allUseAngle.equals("1")) {
- return sDriverMap.get(OpenGlDriverChoice.ANGLE);
+ if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) {
+ Log.v(TAG, "Turn on ANGLE for all applications.");
+ return ANGLE_GL_DRIVER_CHOICE_ANGLE;
+ }
+
+ // Make sure we have a good package name
+ if (TextUtils.isEmpty(packageName)) {
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
final ContentResolver contentResolver = context.getContentResolver();
@@ -400,25 +409,21 @@ public class GraphicsEnvironment {
getGlobalSettingsString(contentResolver, bundle,
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES);
- // Make sure we have a good package name
- if ((packageName == null) || (packageName.isEmpty())) {
- return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
- }
// Make sure we have good settings to use
if (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size()) {
Log.w(TAG,
"Global.Settings values are invalid: "
- + "globalSettingsDriverPkgs.size = "
+ + "number of packages: "
+ globalSettingsDriverPkgs.size() + ", "
- + "globalSettingsDriverValues.size = "
+ + "number of values: "
+ globalSettingsDriverValues.size());
- return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
final int pkgIndex = getGlobalSettingsPkgIndex(packageName, globalSettingsDriverPkgs);
if (pkgIndex < 0) {
- return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
return globalSettingsDriverValues.get(pkgIndex);
@@ -446,27 +451,28 @@ public class GraphicsEnvironment {
}
/**
- * Check for ANGLE debug package, but only for apps that can load them (dumpable)
+ * Check for ANGLE debug package, but only for apps that can load them.
+ * An application can load ANGLE debug package if it is a debuggable application, or
+ * the device is debuggable.
*/
private String getAngleDebugPackage(Context context, Bundle coreSettings) {
- if (isDebuggable()) {
- String debugPackage;
-
- if (coreSettings != null) {
- debugPackage =
- coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
- } else {
- ContentResolver contentResolver = context.getContentResolver();
- debugPackage = Settings.Global.getString(contentResolver,
- Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
- }
-
- if ((debugPackage != null) && (!debugPackage.isEmpty())) {
- return debugPackage;
- }
+ if (!isDebuggable()) {
+ return "";
}
+ final String debugPackage;
- return "";
+ if (coreSettings != null) {
+ debugPackage =
+ coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+ } else {
+ ContentResolver contentResolver = context.getContentResolver();
+ debugPackage = Settings.Global.getString(contentResolver,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+ }
+ if (TextUtils.isEmpty(debugPackage)) {
+ return "";
+ }
+ return debugPackage;
}
/**
@@ -491,7 +497,7 @@ public class GraphicsEnvironment {
final String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
- if ((angleTempRules == null) || angleTempRules.isEmpty()) {
+ if (TextUtils.isEmpty(angleTempRules)) {
Log.v(TAG, "System property '" + ANGLE_TEMP_RULES + "' is not set or is empty");
return false;
}
@@ -583,11 +589,12 @@ public class GraphicsEnvironment {
*
* @param context
* @param bundle
- * @param packageName
+ * @param pm
+ * @param packageName - package name of the application.
* @return true: ANGLE setup successfully
* false: ANGLE not setup (not on allowlist, ANGLE not present, etc.)
*/
- public boolean setupAngle(Context context, Bundle bundle, PackageManager pm,
+ private boolean setupAngle(Context context, Bundle bundle, PackageManager pm,
String packageName) {
if (!shouldUseAngle(context, bundle, packageName)) {
@@ -612,18 +619,18 @@ public class GraphicsEnvironment {
// Otherwise, check to see if ANGLE is properly installed
if (angleInfo == null) {
anglePkgName = getAnglePackageName(pm);
- if (!anglePkgName.isEmpty()) {
- Log.i(TAG, "ANGLE package enabled: " + anglePkgName);
- try {
- // Production ANGLE libraries must be pre-installed as a system app
- angleInfo = pm.getApplicationInfo(anglePkgName,
- PackageManager.MATCH_SYSTEM_ONLY);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed");
- return false;
- }
- } else {
- Log.e(TAG, "Failed to find ANGLE package.");
+ if (TextUtils.isEmpty(anglePkgName)) {
+ Log.w(TAG, "Failed to find ANGLE package.");
+ return false;
+ }
+
+ Log.i(TAG, "ANGLE package enabled: " + anglePkgName);
+ try {
+ // Production ANGLE libraries must be pre-installed as a system app
+ angleInfo = pm.getApplicationInfo(anglePkgName,
+ PackageManager.MATCH_SYSTEM_ONLY);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed");
return false;
}
}
@@ -645,7 +652,7 @@ public class GraphicsEnvironment {
// load a driver, GraphicsEnv::getShouldUseAngle() has seen the package name before
// and can confidently answer yes/no based on the previously set developer
// option value.
- final String devOptIn = getDriverForPkg(context, bundle, packageName);
+ final String devOptIn = getDriverForPackage(context, bundle, packageName);
if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) {
// We setup ANGLE with a temp rules file, so we're done here.
@@ -730,18 +737,18 @@ public class GraphicsEnvironment {
final boolean hasPrereleaseDriver = prereleaseDriver != null && !prereleaseDriver.isEmpty();
if (!hasProductionDriver && !hasPrereleaseDriver) {
- if (DEBUG) {
- Log.v(TAG,
- "Neither updatable production driver nor prerelease driver is supported.");
- }
+ Log.v(TAG, "Neither updatable production driver nor prerelease driver is supported.");
return null;
}
- // To minimize risk of driver updates crippling the device beyond user repair, never use an
- // updated driver for privileged or non-updated system apps. Presumably pre-installed apps
- // were tested thoroughly with the pre-installed driver.
+ // To minimize risk of driver updates crippling the device beyond user repair, never use the
+ // updatable drivers for privileged or non-updated system apps. Presumably pre-installed
+ // apps were tested thoroughly with the system driver.
if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) {
- if (DEBUG) Log.v(TAG, "Ignoring driver package for privileged/non-updated system app.");
+ if (DEBUG) {
+ Log.v(TAG,
+ "Ignore updatable driver package for privileged/non-updated system app.");
+ }
return null;
}
@@ -758,13 +765,13 @@ public class GraphicsEnvironment {
// 6. UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST
switch (coreSettings.getInt(Settings.Global.UPDATABLE_DRIVER_ALL_APPS, 0)) {
case UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF:
- if (DEBUG) Log.v(TAG, "updatable driver is turned off on this device.");
+ Log.v(TAG, "The updatable driver is turned off on this device.");
return null;
case UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRODUCTION_DRIVER:
- if (DEBUG) Log.v(TAG, "All apps opt in to use updatable production driver.");
+ Log.v(TAG, "All apps opt in to use updatable production driver.");
return hasProductionDriver ? productionDriver : null;
case UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER:
- if (DEBUG) Log.v(TAG, "All apps opt in to use updatable prerelease driver.");
+ Log.v(TAG, "All apps opt in to use updatable prerelease driver.");
return hasPrereleaseDriver && enablePrereleaseDriver ? prereleaseDriver : null;
case UPDATABLE_DRIVER_GLOBAL_OPT_IN_DEFAULT:
default:
@@ -775,20 +782,20 @@ public class GraphicsEnvironment {
if (getGlobalSettingsString(null, coreSettings,
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS)
.contains(appPackageName)) {
- if (DEBUG) Log.v(TAG, "App opts out for updatable production driver.");
+ Log.v(TAG, "App opts out for updatable production driver.");
return null;
}
if (getGlobalSettingsString(
null, coreSettings, Settings.Global.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS)
.contains(appPackageName)) {
- if (DEBUG) Log.v(TAG, "App opts in for updatable prerelease driver.");
+ Log.v(TAG, "App opts in for updatable prerelease driver.");
return hasPrereleaseDriver && enablePrereleaseDriver ? prereleaseDriver : null;
}
// Early return here since the rest logic is only for updatable production Driver.
if (!hasProductionDriver) {
- if (DEBUG) Log.v(TAG, "Updatable production driver is not supported on the device.");
+ Log.v(TAG, "Updatable production driver is not supported on the device.");
return null;
}
@@ -801,7 +808,7 @@ public class GraphicsEnvironment {
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST);
if (!isOptIn && allowlist.indexOf(UPDATABLE_DRIVER_ALLOWLIST_ALL) != 0
&& !allowlist.contains(appPackageName)) {
- if (DEBUG) Log.v(TAG, "App is not on the allowlist for updatable production driver.");
+ Log.v(TAG, "App is not on the allowlist for updatable production driver.");
return null;
}
@@ -811,7 +818,7 @@ public class GraphicsEnvironment {
&& getGlobalSettingsString(
null, coreSettings, Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST)
.contains(appPackageName)) {
- if (DEBUG) Log.v(TAG, "App is on the denylist for updatable production driver.");
+ Log.v(TAG, "App is on the denylist for updatable production driver.");
return null;
}
@@ -834,7 +841,7 @@ public class GraphicsEnvironment {
driverPackageInfo = pm.getPackageInfo(driverPackageName,
PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
+ Log.w(TAG, "updatable driver package '" + driverPackageName + "' not installed");
return false;
}
@@ -843,7 +850,7 @@ public class GraphicsEnvironment {
final ApplicationInfo driverAppInfo = driverPackageInfo.applicationInfo;
if (driverAppInfo.targetSdkVersion < Build.VERSION_CODES.O) {
if (DEBUG) {
- Log.w(TAG, "updated driver package is not known to be compatible with O");
+ Log.w(TAG, "updatable driver package is not compatible with O");
}
return false;
}
@@ -853,7 +860,7 @@ public class GraphicsEnvironment {
if (DEBUG) {
// This is the normal case for the pre-installed empty driver package, don't spam
if (driverAppInfo.isUpdatedSystemApp()) {
- Log.w(TAG, "updated driver package has no compatible native libraries");
+ Log.w(TAG, "Updatable driver package has no compatible native libraries");
}
}
return false;
@@ -867,11 +874,8 @@ public class GraphicsEnvironment {
.append(abi);
final String paths = sb.toString();
final String sphalLibraries = getSphalLibraries(context, driverPackageName);
- if (DEBUG) {
- Log.v(TAG,
- "gfx driver package search path: " + paths
- + ", required sphal libraries: " + sphalLibraries);
- }
+ Log.v(TAG, "Updatable driver package search path: " + paths
+ + ", required sphal libraries: " + sphalLibraries);
setDriverPathAndSphalLibraries(paths, sphalLibraries);
if (driverAppInfo.metaData == null) {
@@ -880,7 +884,7 @@ public class GraphicsEnvironment {
String driverBuildTime = driverAppInfo.metaData.getString(METADATA_DRIVER_BUILD_TIME);
if (driverBuildTime == null || driverBuildTime.length() <= 1) {
- Log.v(TAG, "com.android.graphics.driver.build_time is not set");
+ Log.w(TAG, "com.android.graphics.driver.build_time is not set");
driverBuildTime = "L0";
}
// driver_build_time in the meta-data is in "L<Unix epoch timestamp>" format. e.g. L123456.
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index a92d91bdefc5..e996809f1299 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -98,6 +98,8 @@ interface IPowerManager
boolean isAmbientDisplaySuppressedForToken(String token);
// returns whether ambient display is suppressed by any app with any token.
boolean isAmbientDisplaySuppressed();
+ // returns whether ambient display is suppressed by the given app with the given token.
+ boolean isAmbientDisplaySuppressedForTokenByApp(String token, int appUid);
// Forces the system to suspend even if there are held wakelocks.
boolean forceSuspend();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 3265829e5061..50f0c28cb8f8 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -2191,6 +2191,27 @@ public final class PowerManager {
}
/**
+ * Returns true if ambient display is suppressed by the given {@code appUid} with the given
+ * {@code token}.
+ *
+ * <p>This method will return false if {@link #isAmbientDisplayAvailable()} is false.
+ *
+ * @param token The identifier of the ambient display suppression.
+ * @param appUid The uid of the app that suppressed ambient display.
+ * @hide
+ */
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.READ_DREAM_STATE,
+ android.Manifest.permission.READ_DREAM_SUPPRESSION })
+ public boolean isAmbientDisplaySuppressedForTokenByApp(@NonNull String token, int appUid) {
+ try {
+ return mService.isAmbientDisplaySuppressedForTokenByApp(token, appUid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the reason the phone was last shutdown. Calling app must have the
* {@link android.Manifest.permission#DEVICE_POWER} permission to request this information.
* @return Reason for shutdown as an int, {@link #SHUTDOWN_REASON_UNKNOWN} if the file could
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index b654707a683b..35e7bad83736 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -235,6 +235,21 @@ public final class ServiceManager {
}
/**
+ * Returns the list of declared instances for an interface.
+ *
+ * @return true if the service is declared somewhere (eg. VINTF manifest) and
+ * waitForService should always be able to return the service.
+ */
+ public static String[] getDeclaredInstances(@NonNull String iface) {
+ try {
+ return getIServiceManager().getDeclaredInstances(iface);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in getDeclaredInstances", e);
+ return null;
+ }
+ }
+
+ /**
* Returns the specified service from the service manager.
*
* If the service is not running, servicemanager will attempt to start it, and this function
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 91b56fbbc38e..b70b6b5d209e 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -90,6 +90,10 @@ class ServiceManagerProxy implements IServiceManager {
return mServiceManager.isDeclared(name);
}
+ public String[] getDeclaredInstances(String iface) throws RemoteException {
+ return mServiceManager.getDeclaredInstances(iface);
+ }
+
public void registerClientCallback(String name, IBinder service, IClientCallback cb)
throws RemoteException {
throw new RemoteException();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 07867e2a9d9b..e4d1fabfffd8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14464,6 +14464,15 @@ public final class Settings {
*/
public static final String NR_NSA_TRACKING_SCREEN_OFF_MODE =
"nr_nsa_tracking_screen_off_mode";
+
+ /**
+ * Whether to show People Space.
+ * Values are:
+ * 0: Disabled (default)
+ * 1: Enabled
+ * @hide
+ */
+ public static final String SHOW_PEOPLE_SPACE = "show_people_space";
}
/**
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 19860eb45fbf..0f46ffcb2d7f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -879,7 +879,7 @@ public abstract class WallpaperService extends Service {
com.android.internal.R.style.Animation_Wallpaper;
InputChannel inputChannel = new InputChannel();
- if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
+ if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE,
mDisplay.getDisplayId(), mWinFrames.frame, mWinFrames.contentInsets,
mWinFrames.stableInsets, mWinFrames.displayCutout, inputChannel,
mInsetsState, mTempControls) < 0) {
@@ -903,7 +903,7 @@ public abstract class WallpaperService extends Service {
}
final int relayoutResult = mSession.relayout(
- mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
+ mWindow, mLayout, mWidth, mHeight,
View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl,
mInsetsState, mTempControls, mSurfaceSize, mTmpSurfaceControl);
if (mSurfaceControl.isValid()) {
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 2ded473930f7..7a117f14d9b5 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -227,7 +227,7 @@ public final class Log {
* @param level The level to check.
* @return Whether or not that this is allowed to be logged.
* @throws IllegalArgumentException is thrown if the tag.length() > 23
- * for Nougat (7.0) releases (API <= 23) and prior, there is no
+ * for Nougat (7.0) and prior releases (API <= 25), there is no
* tag limit of concern after this API level.
*/
@FastNative
diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java
new file mode 100644
index 000000000000..865d5608a40a
--- /dev/null
+++ b/core/java/android/util/imetracing/ImeTracing.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imetracing;
+
+import android.app.ActivityThread;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.ShellCommand;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.view.IInputMethodManager;
+
+/**
+ *
+ * An abstract class that declares the methods for ime trace related operations - enable trace,
+ * schedule trace and add new trace to buffer. Both the client and server side classes can use
+ * it by getting an implementation through {@link ImeTracing#getInstance()}.
+ *
+ * @hide
+ */
+public abstract class ImeTracing {
+
+ static final String TAG = "imeTracing";
+ public static final String PROTO_ARG = "--proto-com-android-imetracing";
+
+ private static ImeTracing sInstance;
+ static boolean sEnabled = false;
+ IInputMethodManager mService;
+
+ ImeTracing() throws ServiceNotFoundException {
+ mService = IInputMethodManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
+ }
+
+ /**
+ * Returns an instance of {@link ImeTracingServerImpl} when called from a server side class
+ * and an instance of {@link ImeTracingClientImpl} when called from a client side class.
+ * Useful to schedule a dump for next frame or save a dump when certain methods are called.
+ *
+ * @return Instance of one of the children classes of {@link ImeTracing}
+ */
+ public static ImeTracing getInstance() {
+ if (sInstance == null) {
+ try {
+ sInstance = isSystemProcess()
+ ? new ImeTracingServerImpl() : new ImeTracingClientImpl();
+ } catch (RemoteException | ServiceNotFoundException e) {
+ Log.e(TAG, "Exception while creating ImeTracing instance", e);
+ }
+ }
+ return sInstance;
+ }
+
+ /**
+ * Sends request to start proto dump to {@link ImeTracingServerImpl} when called from a
+ * server process and to {@link ImeTracingClientImpl} when called from a client process.
+ */
+ public abstract void triggerDump();
+
+ /**
+ * @param proto dump to be added to the buffer
+ */
+ public abstract void addToBuffer(ProtoOutputStream proto);
+
+ /**
+ * @param shell The shell command to process
+ * @return {@code 0} if the command was successfully processed, {@code -1} otherwise
+ */
+ public abstract int onShellCommand(ShellCommand shell);
+
+ /**
+ * Sets whether ime tracing is enabled.
+ *
+ * @param enabled Tells whether ime tracing should be enabled or disabled.
+ */
+ public void setEnabled(boolean enabled) {
+ sEnabled = enabled;
+ }
+
+ /**
+ * @return {@code true} if dumping is enabled, {@code false} otherwise.
+ */
+ public boolean isEnabled() {
+ return sEnabled;
+ }
+
+ /**
+ * @return {@code true} if tracing is available, {@code false} otherwise.
+ */
+ public boolean isAvailable() {
+ return mService != null;
+ }
+
+ private static boolean isSystemProcess() {
+ return ActivityThread.isSystem();
+ }
+}
diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/android/util/imetracing/ImeTracingClientImpl.java
new file mode 100644
index 000000000000..e5d7d3380d02
--- /dev/null
+++ b/core/java/android/util/imetracing/ImeTracingClientImpl.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imetracing;
+
+import android.os.RemoteException;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.ShellCommand;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.inputmethod.InputMethodManager;
+
+/**
+ * @hide
+ */
+class ImeTracingClientImpl extends ImeTracing {
+
+ private boolean mDumpInProgress;
+ private final Object mDumpInProgressLock = new Object();
+
+ ImeTracingClientImpl() throws ServiceNotFoundException, RemoteException {
+ sEnabled = mService.isImeTraceEnabled();
+ }
+
+ @Override
+ public void addToBuffer(ProtoOutputStream proto) {
+ }
+
+ @Override
+ public int onShellCommand(ShellCommand shell) {
+ return -1;
+ }
+
+ @Override
+ public void triggerDump() {
+ if (isAvailable() && isEnabled()) {
+ boolean doDump = false;
+ synchronized (mDumpInProgressLock) {
+ if (!mDumpInProgress) {
+ mDumpInProgress = true;
+ doDump = true;
+ }
+ }
+
+ if (doDump) {
+ try {
+ ProtoOutputStream proto = new ProtoOutputStream();
+ InputMethodManager.dumpProto(proto);
+ mService.startProtoDump(proto.getBytes());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while sending ime-related client dump to server", e);
+ } finally {
+ mDumpInProgress = false;
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/android/util/imetracing/ImeTracingServerImpl.java
new file mode 100644
index 000000000000..350cf5721148
--- /dev/null
+++ b/core/java/android/util/imetracing/ImeTracingServerImpl.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imetracing;
+
+import static android.os.Build.IS_USER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.MAGIC_NUMBER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.MAGIC_NUMBER_H;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.MAGIC_NUMBER_L;
+
+import android.os.RemoteException;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.ShellCommand;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.TraceBuffer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @hide
+ */
+class ImeTracingServerImpl extends ImeTracing {
+ private static final String TRACE_FILENAME = "/data/misc/wmtrace/ime_trace.pb";
+ private static final int BUFFER_CAPACITY = 4096 * 1024;
+
+ // Needed for winscope to auto-detect the dump type. Explained further in
+ // core.proto.android.view.inputmethod.inputmethodeditortrace.proto
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ private final TraceBuffer mBuffer;
+ private final File mTraceFile;
+ private final Object mEnabledLock = new Object();
+
+ ImeTracingServerImpl() throws ServiceNotFoundException {
+ mBuffer = new TraceBuffer<>(BUFFER_CAPACITY);
+ mTraceFile = new File(TRACE_FILENAME);
+ }
+
+ /**
+ * The provided dump is added to the current dump buffer {@link ImeTracingServerImpl#mBuffer}.
+ *
+ * @param proto dump to be added to the buffer
+ */
+ @Override
+ public void addToBuffer(ProtoOutputStream proto) {
+ if (isAvailable() && isEnabled()) {
+ mBuffer.add(proto);
+ }
+ }
+
+ /**
+ * Responds to a shell command of the format "adb shell cmd input_method ime tracing <command>"
+ *
+ * @param shell The shell command to process
+ * @return {@code 0} if the command was valid and successfully processed, {@code -1} otherwise
+ */
+ @Override
+ public int onShellCommand(ShellCommand shell) {
+ PrintWriter pw = shell.getOutPrintWriter();
+ String cmd = shell.getNextArgRequired();
+ switch (cmd) {
+ case "start":
+ startTrace(pw);
+ return 0;
+ case "stop":
+ stopTrace(pw);
+ return 0;
+ default:
+ pw.println("Unknown command: " + cmd);
+ pw.println("Input method trace options:");
+ pw.println(" start: Start tracing");
+ pw.println(" stop: Stop tracing");
+ return -1;
+ }
+ }
+
+ @Override
+ public void triggerDump() {
+ if (isAvailable() && isEnabled()) {
+ try {
+ mService.startProtoDump(null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while triggering proto dump", e);
+ }
+ }
+ }
+
+ private void writeTraceToFileLocked() {
+ try {
+ ProtoOutputStream proto = new ProtoOutputStream();
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ mBuffer.writeTraceToFile(mTraceFile, proto);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to write buffer to file", e);
+ }
+ }
+
+ @GuardedBy("mEnabledLock")
+ private void startTrace(PrintWriter pw) {
+ if (IS_USER) {
+ Log.w(TAG, "Warn: Tracing is not supported on user builds.");
+ return;
+ }
+
+ synchronized (mEnabledLock) {
+ if (isAvailable() && isEnabled()) {
+ Log.w(TAG, "Warn: Tracing is already started.");
+ return;
+ }
+
+ pw.println("Starting tracing to " + mTraceFile + ".");
+ sEnabled = true;
+ mBuffer.resetBuffer();
+ }
+ }
+
+ @GuardedBy("mEnabledLock")
+ private void stopTrace(PrintWriter pw) {
+ if (IS_USER) {
+ Log.w(TAG, "Warn: Tracing is not supported on user builds.");
+ return;
+ }
+
+ synchronized (mEnabledLock) {
+ if (!isAvailable() || !isEnabled()) {
+ Log.w(TAG, "Warn: Tracing is not available or not started.");
+ return;
+ }
+
+ pw.println("Stopping tracing and writing traces to " + mTraceFile + ".");
+ sEnabled = false;
+ writeTraceToFileLocked();
+ mBuffer.resetBuffer();
+ }
+ }
+}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 94e641c62b25..193e674dd1b0 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -120,12 +120,6 @@ oneway interface IWindow {
void updatePointerIcon(float x, float y);
/**
- * System chrome visibility changes
- */
- void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
- int localValue, int localChanges);
-
- /**
* Called for non-application windows when the enter animation has completed.
*/
void dispatchWindowShown();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 8e875d7a889d..daab70ae336f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -378,11 +378,6 @@ interface IWindowManager
boolean requestAssistScreenshot(IAssistDataReceiver receiver);
/**
- * Called by the status bar to notify Views of changes to System UI visiblity.
- */
- oneway void statusBarVisibilityChanged(int displayId, int visibility);
-
- /**
* Called by System UI to notify Window Manager to hide transient bars.
*/
oneway void hideTransientBars(int displayId);
@@ -757,4 +752,10 @@ interface IWindowManager
*/
void requestScrollCapture(int displayId, IBinder behindClient, int taskId,
IScrollCaptureController controller);
+
+ /**
+ * Holds the WM lock for the specified amount of milliseconds.
+ * Intended for use by the tests that need to imitate lock contention.
+ */
+ void holdLock(in int durationMs);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 18c87c092569..69a5fafbb0db 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -44,17 +44,17 @@ import java.util.List;
* {@hide}
*/
interface IWindowSession {
- int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
+ int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outFrame,
out Rect outContentInsets, out Rect outStableInsets,
out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
out InsetsState insetsState, out InsetsSourceControl[] activeControls);
- int addToDisplayAsUser(IWindow window, int seq, in WindowManager.LayoutParams attrs,
+ int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in int userId,
out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets,
out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
out InsetsState insetsState, out InsetsSourceControl[] activeControls);
- int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
+ int addToDisplayWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outContentInsets,
out Rect outStableInsets, out InsetsState insetsState);
@UnsupportedAppUsage
@@ -68,7 +68,6 @@ interface IWindowSession {
* to draw the window's contents.
*
* @param window The window being modified.
- * @param seq Ordering sequence number.
* @param attrs If non-null, new attributes to apply to the window.
* @param requestedWidth The width the window wants to be.
* @param requestedHeight The height the window wants to be.
@@ -106,7 +105,7 @@ interface IWindowSession {
* @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
* {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
*/
- int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
+ int relayout(IWindow window, in WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility,
int flags, long frameNumber, out ClientWindowFrames outFrames,
out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 92772c1d7a44..efc0bd2785f4 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -16,16 +16,23 @@
package android.view;
+import static android.view.ImeFocusControllerProto.HAS_IME_FOCUS;
+import static android.view.ImeFocusControllerProto.NEXT_SERVED_VIEW;
+import static android.view.ImeFocusControllerProto.SERVED_VIEW;
+
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.UiThread;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
+import java.util.Objects;
+
/**
* Responsible for IME focus handling inside {@link ViewRootImpl}.
* @hide
@@ -280,4 +287,12 @@ public final class ImeFocusController {
boolean hasImeFocus() {
return mHasImeFocus;
}
+
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HAS_IME_FOCUS, mHasImeFocus);
+ proto.write(SERVED_VIEW, Objects.toString(mServedView));
+ proto.write(NEXT_SERVED_VIEW, Objects.toString(mNextServedView));
+ proto.end(token);
+ }
}
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 82f60366a814..dd1a19458e1d 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -16,6 +16,9 @@
package android.view;
+import static android.view.ImeInsetsSourceConsumerProto.FOCUSED_EDITOR;
+import static android.view.ImeInsetsSourceConsumerProto.INSETS_SOURCE_CONSUMER;
+import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsState.ITYPE_IME;
@@ -24,6 +27,7 @@ import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.Parcel;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl.Transaction;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
@@ -111,7 +115,6 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
public @ShowResult int requestShow(boolean fromIme) {
// TODO: ResultReceiver for IME.
// TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
-
if (getControl() == null) {
// If control is null, schedule to show IME when control is available.
mIsRequestedVisibleAwaitingControl = true;
@@ -227,6 +230,17 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
return Arrays.equals(parcel1.createByteArray(), parcel2.createByteArray());
}
+ @Override
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ super.dumpDebug(proto, INSETS_SOURCE_CONSUMER);
+ if (mFocusedEditor != null) {
+ mFocusedEditor.dumpDebug(proto, FOCUSED_EDITOR);
+ }
+ proto.write(IS_REQUESTED_VISIBLE_AWAITING_CONTROL, mIsRequestedVisibleAwaitingControl);
+ proto.end(token);
+ }
+
private InputMethodManager getImm() {
return mController.getHost().getInputMethodManager();
}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 6ffd892e4351..71899fab554c 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -17,6 +17,14 @@
package android.view;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.InsetsAnimationControlImplProto.CURRENT_ALPHA;
+import static android.view.InsetsAnimationControlImplProto.IS_CANCELLED;
+import static android.view.InsetsAnimationControlImplProto.IS_FINISHED;
+import static android.view.InsetsAnimationControlImplProto.PENDING_ALPHA;
+import static android.view.InsetsAnimationControlImplProto.PENDING_FRACTION;
+import static android.view.InsetsAnimationControlImplProto.PENDING_INSETS;
+import static android.view.InsetsAnimationControlImplProto.SHOWN_ON_FINISH;
+import static android.view.InsetsAnimationControlImplProto.TMP_MATRIX;
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsController.DEBUG;
@@ -38,6 +46,8 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseSetArray;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsSide;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
@@ -48,6 +58,7 @@ import android.view.animation.Interpolator;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Implements {@link WindowInsetsAnimationController}
@@ -122,6 +133,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
mAnimationType = animationType;
mController.startAnimation(this, listener, types, mAnimation,
new Bounds(mHiddenInsets, mShownInsets));
+
+ if ((mTypes & WindowInsets.Type.ime()) != 0) {
+ ImeTracing.getInstance().triggerDump();
+ }
}
private boolean calculatePerceptible(Insets currentInsets, float currentAlpha) {
@@ -285,6 +300,20 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
return mAnimation;
}
+ @Override
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(IS_CANCELLED, mCancelled);
+ proto.write(IS_FINISHED, mFinished);
+ proto.write(TMP_MATRIX, Objects.toString(mTmpMatrix));
+ proto.write(PENDING_INSETS, Objects.toString(mPendingInsets));
+ proto.write(PENDING_FRACTION, mPendingFraction);
+ proto.write(SHOWN_ON_FINISH, mShownOnFinish);
+ proto.write(CURRENT_ALPHA, mCurrentAlpha);
+ proto.write(PENDING_ALPHA, mPendingAlpha);
+ proto.end(token);
+ }
+
WindowInsetsAnimationControlListener getListener() {
return mListener;
}
diff --git a/core/java/android/view/InsetsAnimationControlRunner.java b/core/java/android/view/InsetsAnimationControlRunner.java
index 0711c3e166d8..0275b521a2a2 100644
--- a/core/java/android/view/InsetsAnimationControlRunner.java
+++ b/core/java/android/view/InsetsAnimationControlRunner.java
@@ -16,6 +16,7 @@
package android.view;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsController.AnimationType;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsets.Type.InsetsType;
@@ -53,4 +54,14 @@ public interface InsetsAnimationControlRunner {
* @return The animation type this runner is running.
*/
@AnimationType int getAnimationType();
+
+ /**
+ *
+ * Export the state of classes that implement this interface into a protocol buffer
+ * output stream.
+ *
+ * @param proto Stream to write the state to
+ * @param fieldId FieldId of the implementation class
+ */
+ void dumpDebug(ProtoOutputStream proto, long fieldId);
}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 123604489da4..cc3cd278b267 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.os.Trace;
import android.util.Log;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsController.AnimationType;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
@@ -122,6 +123,12 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
@Override
@UiThread
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ mControl.dumpDebug(proto, fieldId);
+ }
+
+ @Override
+ @UiThread
public int getTypes() {
return mControl.getTypes();
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 92eade3affaa..652781a310b9 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.InsetsControllerProto.CONTROL;
+import static android.view.InsetsControllerProto.STATE;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.toInternalType;
@@ -41,6 +43,8 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
@@ -298,6 +302,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Override
public void onReady(WindowInsetsAnimationController controller, int types) {
+ if ((types & ime()) != 0) {
+ ImeTracing.getInstance().triggerDump();
+ }
+
mController = controller;
if (DEBUG) Log.d(TAG, "default animation onReady types: " + types);
@@ -812,6 +820,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@VisibleForTesting
public void show(@InsetsType int types, boolean fromIme) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
// Handle pending request ready in case there was one set.
if (fromIme && mPendingImeControlRequest != null) {
PendingControlRequest pendingRequest = mPendingImeControlRequest;
@@ -860,6 +871,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
void hide(@InsetsType int types, boolean fromIme) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
int typesReady = 0;
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
@@ -894,6 +908,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
listener.onCancelled(null);
return;
}
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
interpolator, animationType, getLayoutInsetsDuringAnimationMode(types),
@@ -1292,6 +1309,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private void hideDirectly(
@InsetsType int types, boolean animationFinished, @AnimationType int animationType) {
+ if ((types & ime()) != 0) {
+ ImeTracing.getInstance().triggerDump();
+ }
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType);
@@ -1299,6 +1319,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
private void showDirectly(@InsetsType int types) {
+ if ((types & ime()) != 0) {
+ ImeTracing.getInstance().triggerDump();
+ }
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
@@ -1318,6 +1341,16 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
mState.dump(prefix + " ", pw);
}
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ mState.dumpDebug(proto, STATE);
+ for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+ InsetsAnimationControlRunner runner = mRunningAnimations.get(i).runner;
+ runner.dumpDebug(proto, CONTROL);
+ }
+ proto.end(token);
+ }
+
@VisibleForTesting
@Override
public void startAnimation(InsetsAnimationControlImpl controller,
@@ -1388,7 +1421,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
@Override
- public @Appearance int getSystemBarsBehavior() {
+ public @Behavior int getSystemBarsBehavior() {
return mHost.getSystemBarsBehavior();
}
diff --git a/core/java/android/view/InsetsFlags.java b/core/java/android/view/InsetsFlags.java
index 385b0bf960ef..5a64a5d5b3a6 100644
--- a/core/java/android/view/InsetsFlags.java
+++ b/core/java/android/view/InsetsFlags.java
@@ -16,13 +16,6 @@
package android.view;
-import static android.view.View.NAVIGATION_BAR_TRANSLUCENT;
-import static android.view.View.NAVIGATION_BAR_TRANSPARENT;
-import static android.view.View.STATUS_BAR_TRANSLUCENT;
-import static android.view.View.STATUS_BAR_TRANSPARENT;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
-import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
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;
@@ -76,44 +69,4 @@ public class InsetsFlags {
name = "SHOW_TRANSIENT_BARS_BY_SWIPE")
})
public @Behavior int behavior;
-
- /**
- * Converts system UI visibility to appearance.
- *
- * @param systemUiVisibility the system UI visibility to be converted.
- * @return the outcome {@link Appearance}
- */
- public static @Appearance int getAppearance(int systemUiVisibility) {
- int appearance = 0;
- appearance |= convertFlag(systemUiVisibility, SYSTEM_UI_FLAG_LOW_PROFILE,
- APPEARANCE_LOW_PROFILE_BARS);
- appearance |= convertFlag(systemUiVisibility, SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
- APPEARANCE_LIGHT_STATUS_BARS);
- appearance |= convertFlag(systemUiVisibility, SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- APPEARANCE_LIGHT_NAVIGATION_BARS);
- appearance |= convertNoFlag(systemUiVisibility,
- STATUS_BAR_TRANSLUCENT | STATUS_BAR_TRANSPARENT, APPEARANCE_OPAQUE_STATUS_BARS);
- appearance |= convertNoFlag(systemUiVisibility,
- NAVIGATION_BAR_TRANSLUCENT | NAVIGATION_BAR_TRANSPARENT,
- APPEARANCE_OPAQUE_NAVIGATION_BARS);
- return appearance;
- }
-
- /**
- * Converts the system UI visibility into an appearance flag if the given visibility contains
- * the given system UI flag.
- */
- private static @Appearance int convertFlag(int systemUiVisibility, int systemUiFlag,
- @Appearance int appearance) {
- return (systemUiVisibility & systemUiFlag) != 0 ? appearance : 0;
- }
-
- /**
- * Converts the system UI visibility into an appearance flag if the given visibility doesn't
- * contains the given system UI flag.
- */
- private static @Appearance int convertNoFlag(int systemUiVisibility, int systemUiFlag,
- @Appearance int appearance) {
- return (systemUiVisibility & systemUiFlag) == 0 ? appearance : 0;
- }
}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index dbf75705c073..41cc8459a266 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -16,6 +16,10 @@
package android.view;
+import static android.view.InsetsSourceProto.FRAME;
+import static android.view.InsetsSourceProto.TYPE;
+import static android.view.InsetsSourceProto.VISIBLE;
+import static android.view.InsetsSourceProto.VISIBLE_FRAME;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
@@ -25,6 +29,7 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
import java.io.PrintWriter;
@@ -183,6 +188,17 @@ public class InsetsSource implements Parcelable {
return false;
}
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(TYPE, InsetsState.typeToString(mType));
+ mFrame.dumpDebug(proto, FRAME);
+ if (mVisibleFrame != null) {
+ mVisibleFrame.dumpDebug(proto, VISIBLE_FRAME);
+ }
+ proto.write(VISIBLE, mVisible);
+ proto.end(token);
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType));
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index ba40459692f7..d7ceaf792198 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -19,6 +19,13 @@ package android.view;
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsController.DEBUG;
+import static android.view.InsetsSourceConsumerProto.HAS_WINDOW_FOCUS;
+import static android.view.InsetsSourceConsumerProto.INTERNAL_INSETS_TYPE;
+import static android.view.InsetsSourceConsumerProto.IS_REQUESTED_VISIBLE;
+import static android.view.InsetsSourceConsumerProto.PENDING_FRAME;
+import static android.view.InsetsSourceConsumerProto.PENDING_VISIBLE_FRAME;
+import static android.view.InsetsSourceConsumerProto.SOURCE_CONTROL;
+import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.getDefaultVisibility;
import static android.view.InsetsState.toPublicType;
@@ -28,6 +35,8 @@ import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.util.Log;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type.InsetsType;
@@ -319,6 +328,9 @@ public class InsetsSourceConsumer {
@VisibleForTesting(visibility = PACKAGE)
public boolean notifyAnimationFinished() {
+ if (mType == ITYPE_IME) {
+ ImeTracing.getInstance().triggerDump();
+ }
if (mPendingFrame != null) {
InsetsSource source = mState.getSource(mType);
source.setFrame(mPendingFrame);
@@ -360,4 +372,21 @@ public class InsetsSourceConsumer {
t.apply();
onPerceptible(mRequestedVisible);
}
+
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(INTERNAL_INSETS_TYPE, InsetsState.typeToString(mType));
+ proto.write(HAS_WINDOW_FOCUS, mHasWindowFocus);
+ proto.write(IS_REQUESTED_VISIBLE, mRequestedVisible);
+ if (mSourceControl != null) {
+ mSourceControl.dumpDebug(proto, SOURCE_CONTROL);
+ }
+ if (mPendingFrame != null) {
+ mPendingFrame.dumpDebug(proto, PENDING_FRAME);
+ }
+ if (mPendingVisibleFrame != null) {
+ mPendingVisibleFrame.dumpDebug(proto, PENDING_VISIBLE_FRAME);
+ }
+ proto.end(token);
+ }
}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 51b49214387a..b45bd3869f64 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -16,10 +16,17 @@
package android.view;
+import static android.graphics.PointProto.X;
+import static android.graphics.PointProto.Y;
+import static android.view.InsetsSourceControlProto.LEASH;
+import static android.view.InsetsSourceControlProto.POSITION;
+import static android.view.InsetsSourceControlProto.TYPE;
+
import android.annotation.Nullable;
import android.graphics.Point;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
import java.io.PrintWriter;
@@ -120,4 +127,19 @@ public class InsetsSourceControl implements Parcelable {
return new InsetsSourceControl[size];
}
};
+
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(TYPE, InsetsState.typeToString(mType));
+
+ final long surfaceToken = proto.start(POSITION);
+ proto.write(X, mSurfacePosition.x);
+ proto.write(Y, mSurfacePosition.y);
+ proto.end(surfaceToken);
+
+ if (mLeash != null) {
+ mLeash.dumpDebug(proto, LEASH);
+ }
+ proto.end(token);
+ }
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 5f8b5e5dde33..eabb71851303 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.InsetsStateProto.DISPLAY_FRAME;
+import static android.view.InsetsStateProto.SOURCES;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
@@ -41,6 +43,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
@@ -458,9 +461,11 @@ public class InsetsState implements Parcelable {
final ArraySet<Integer> result = new ArraySet<>();
if ((types & Type.STATUS_BARS) != 0) {
result.add(ITYPE_STATUS_BAR);
+ result.add(ITYPE_CLIMATE_BAR);
}
if ((types & Type.NAVIGATION_BARS) != 0) {
result.add(ITYPE_NAVIGATION_BAR);
+ result.add(ITYPE_EXTRA_NAVIGATION_BAR);
}
if ((types & Type.CAPTION_BAR) != 0) {
result.add(ITYPE_CAPTION_BAR);
@@ -543,6 +548,16 @@ public class InsetsState implements Parcelable {
}
}
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ InsetsSource source = mSources[ITYPE_IME];
+ if (source != null) {
+ source.dumpDebug(proto, SOURCES);
+ }
+ mDisplayFrame.dumpDebug(proto, DISPLAY_FRAME);
+ proto.end(token);
+ }
+
public static String typeToString(@InternalInsetsType int type) {
switch (type) {
case ITYPE_STATUS_BAR:
diff --git a/core/java/android/view/OnReceiveContentCallback.java b/core/java/android/view/OnReceiveContentCallback.java
new file mode 100644
index 000000000000..73bcb93d39d0
--- /dev/null
+++ b/core/java/android/view/OnReceiveContentCallback.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.net.Uri;
+import android.os.Bundle;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Callback for apps to implement handling for insertion of content. "Content" here refers to both
+ * text and non-text (plain/styled text, HTML, images, videos, audio files, etc).
+ *
+ * <p>This callback can be attached to different types of UI components using
+ * {@link View#setOnReceiveContentCallback}.
+ *
+ * <p>For editable {@link android.widget.TextView} components, implementations can extend from
+ * {@link android.widget.TextViewOnReceiveContentCallback} to reuse default platform behavior for
+ * handling text.
+ *
+ * <p>Example implementation:<br>
+ * <pre class="prettyprint">
+ * public class MyOnReceiveContentCallback implements OnReceiveContentCallback&lt;TextView&gt; {
+ *
+ * private static final Set&lt;String&gt; SUPPORTED_MIME_TYPES = Collections.unmodifiableSet(
+ * Set.of("image/*", "video/*"));
+ *
+ * &#64;NonNull
+ * &#64;Override
+ * public Set&lt;String&gt; getSupportedMimeTypes() {
+ * return SUPPORTED_MIME_TYPES;
+ * }
+ *
+ * &#64;Override
+ * public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) {
+ * // ... app-specific logic to handle the content in the payload ...
+ * }
+ * }
+ * </pre>
+ *
+ * @param <T> The type of {@link View} with which this receiver can be associated.
+ */
+public interface OnReceiveContentCallback<T extends View> {
+ /**
+ * Receive the given content.
+ *
+ * <p>This function will only be invoked if the MIME type of the content is in the set of
+ * types returned by {@link #getSupportedMimeTypes}.
+ *
+ * <p>For text, if the view has a selection, the selection should be overwritten by the clip; if
+ * there's no selection, this method should insert the content at the current cursor position.
+ *
+ * <p>For non-text content (e.g. an image), the content may be inserted inline, or it may be
+ * added as an attachment (could potentially be shown in a completely separate view).
+ *
+ * @param view The view where the content insertion was requested.
+ * @param payload The content to insert and related metadata.
+ *
+ * @return Returns true if some or all of the content is accepted for insertion. If accepted,
+ * actual insertion may be handled asynchronously in the background and may or may not result in
+ * successful insertion. For example, the app may not end up inserting an accepted item if it
+ * exceeds the app's size limit for that type of content.
+ */
+ boolean onReceiveContent(@NonNull T view, @NonNull Payload payload);
+
+ /**
+ * Returns the MIME types that can be handled by this callback.
+ *
+ * <p>The {@link #onReceiveContent} callback method will only be invoked if the MIME type of the
+ * content is in the set of supported types returned here.
+ *
+ * <p>Different platform features (e.g. pasting from the clipboard, inserting stickers from the
+ * keyboard, etc) may use this function to conditionally alter their behavior. For example, the
+ * keyboard may choose to hide its UI for inserting GIFs if the input field that has focus has
+ * a {@link OnReceiveContentCallback} set and the MIME types returned from this function don't
+ * include "image/gif".
+ *
+ * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC
+ * MIME types. As a result, you should always write your MIME types with lower case letters, or
+ * use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to lower
+ * case.</em>
+ *
+ * @param view The target view.
+ * @return An immutable set with the MIME types supported by this callback. The returned MIME
+ * types may contain wildcards such as "text/*", "image/*", etc.
+ */
+ @SuppressLint("CallbackMethodName")
+ @NonNull
+ Set<String> getSupportedMimeTypes(@NonNull T view);
+
+ /**
+ * Returns true if at least one of the MIME types of the given clip is
+ * {@link #getSupportedMimeTypes supported} by this receiver.
+ *
+ * @hide
+ */
+ default boolean supports(@NonNull T view, @NonNull ClipDescription description) {
+ for (String supportedMimeType : getSupportedMimeTypes(view)) {
+ if (description.hasMimeType(supportedMimeType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Holds all the relevant data for a request to {@link OnReceiveContentCallback}.
+ */
+ final class Payload {
+
+ /**
+ * Specifies the UI through which content is being inserted.
+ *
+ * @hide
+ */
+ @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD,
+ SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Source {}
+
+ /**
+ * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or
+ * "Paste as plain text" action in the insertion/selection menu).
+ */
+ public static final int SOURCE_CLIPBOARD = 0;
+
+ /**
+ * Specifies that the operation was triggered from the soft keyboard (also known as input
+ * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard
+ * for more info.
+ */
+ public static final int SOURCE_INPUT_METHOD = 1;
+
+ /**
+ * Specifies that the operation was triggered by the drag/drop framework. See
+ * https://developer.android.com/guide/topics/ui/drag-drop for more info.
+ */
+ public static final int SOURCE_DRAG_AND_DROP = 2;
+
+ /**
+ * Specifies that the operation was triggered by the autofill framework. See
+ * https://developer.android.com/guide/topics/text/autofill for more info.
+ */
+ public static final int SOURCE_AUTOFILL = 3;
+
+ /**
+ * Specifies that the operation was triggered by a result from a
+ * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection
+ * menu.
+ */
+ public static final int SOURCE_PROCESS_TEXT = 4;
+
+ /**
+ * Returns the symbolic name of the given source.
+ *
+ * @hide
+ */
+ static String sourceToString(@Source int source) {
+ switch (source) {
+ case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD";
+ case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD";
+ case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP";
+ case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL";
+ case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT";
+ }
+ return String.valueOf(source);
+ }
+
+ /**
+ * Flags to configure the insertion behavior.
+ *
+ * @hide
+ */
+ @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {}
+
+ /**
+ * Flag requesting that the content should be converted to plain text prior to inserting.
+ */
+ public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0;
+
+ /**
+ * Returns the symbolic names of the set flags or {@code "0"} if no flags are set.
+ *
+ * @hide
+ */
+ static String flagsToString(@Flags int flags) {
+ if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) {
+ return "FLAG_CONVERT_TO_PLAIN_TEXT";
+ }
+ return String.valueOf(flags);
+ }
+
+ /**
+ * The data to be inserted.
+ */
+ @NonNull private final ClipData mClip;
+
+ /**
+ * The source of the operation. See {@code SOURCE_} constants.
+ */
+ private final @Source int mSource;
+
+ /**
+ * Optional flags that control the insertion behavior. See {@code FLAG_} constants.
+ */
+ private final @Flags int mFlags;
+
+ /**
+ * Optional http/https URI for the content that may be provided by the IME. This is only
+ * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty
+ * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the
+ * IME.
+ */
+ @Nullable
+ private final Uri mLinkUri;
+
+ /**
+ * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will
+ * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by
+ * the IME.
+ */
+ @Nullable
+ private final Bundle mExtras;
+
+ private Payload(Builder b) {
+ this.mClip = Objects.requireNonNull(b.mClip);
+ this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_PROCESS_TEXT,
+ "source");
+ this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT);
+ this.mLinkUri = b.mLinkUri;
+ this.mExtras = b.mExtras;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "Payload{"
+ + "clip=" + mClip.getDescription()
+ + ", source=" + sourceToString(mSource)
+ + ", flags=" + flagsToString(mFlags)
+ + ", linkUri=" + mLinkUri
+ + ", extras=" + mExtras
+ + "}";
+ }
+
+ /**
+ * The data to be inserted.
+ */
+ public @NonNull ClipData getClip() {
+ return mClip;
+ }
+
+ /**
+ * The source of the operation. See {@code SOURCE_} constants.
+ */
+ public @Source int getSource() {
+ return mSource;
+ }
+
+ /**
+ * Optional flags that control the insertion behavior. See {@code FLAG_} constants.
+ */
+ public @Flags int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Optional http/https URI for the content that may be provided by the IME. This is only
+ * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty
+ * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the
+ * IME.
+ */
+ public @Nullable Uri getLinkUri() {
+ return mLinkUri;
+ }
+
+ /**
+ * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will
+ * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by
+ * the IME.
+ */
+ public @Nullable Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Builder for {@link Payload}.
+ */
+ public static final class Builder {
+ @NonNull private final ClipData mClip;
+ private final @Source int mSource;
+ private @Flags int mFlags;
+ @Nullable private Uri mLinkUri;
+ @Nullable private Bundle mExtras;
+
+ /**
+ * Creates a new builder.
+ * @param clip The data to insert.
+ * @param source The source of the operation. See {@code SOURCE_} constants.
+ */
+ public Builder(@NonNull ClipData clip, @Source int source) {
+ mClip = clip;
+ mSource = source;
+ }
+
+ /**
+ * Sets flags that control content insertion behavior.
+ * @param flags Optional flags to configure the insertion behavior. Use 0 for default
+ * behavior. See {@code FLAG_} constants.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setFlags(@Flags int flags) {
+ mFlags = flags;
+ return this;
+ }
+
+ /**
+ * Sets the http/https URI for the content. See
+ * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info.
+ * @param linkUri Optional http/https URI for the content.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setLinkUri(@Nullable Uri linkUri) {
+ mLinkUri = linkUri;
+ return this;
+ }
+
+ /**
+ * Sets additional metadata.
+ * @param extras Optional bundle with additional metadata.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * @return A new {@link Payload} instance with the data from this builder.
+ */
+ @NonNull
+ public Payload build() {
+ return new Payload(this);
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e7e28ac98234..c430a4d57338 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3995,89 +3995,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
- * out of the public fields to keep the undefined bits out of the developer's way.
- *
- * Flag to specify that the status bar is displayed in transient mode.
- */
- public static final int STATUS_BAR_TRANSIENT = 0x04000000;
-
- /**
- * @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
- * out of the public fields to keep the undefined bits out of the developer's way.
- *
- * Flag to specify that the navigation bar is displayed in transient mode.
- */
- @UnsupportedAppUsage
- public static final int NAVIGATION_BAR_TRANSIENT = 0x08000000;
-
- /**
- * @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
- * out of the public fields to keep the undefined bits out of the developer's way.
- *
- * Flag to specify that the hidden status bar would like to be shown.
- */
- public static final int STATUS_BAR_UNHIDE = 0x10000000;
-
- /**
- * @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
- * out of the public fields to keep the undefined bits out of the developer's way.
- *
- * Flag to specify that the hidden navigation bar would like to be shown.
- */
- public static final int NAVIGATION_BAR_UNHIDE = 0x20000000;
-
- /**
- * @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
- * out of the public fields to keep the undefined bits out of the developer's way.
- *
- * Flag to specify that the status bar is displayed in translucent mode.
- */
- public static final int STATUS_BAR_TRANSLUCENT = 0x40000000;
-
- /**
- * @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
- * out of the public fields to keep the undefined bits out of the developer's way.
- *
- * Flag to specify that the navigation bar is displayed in translucent mode.
- */
- public static final int NAVIGATION_BAR_TRANSLUCENT = 0x80000000;
-
- /**
- * @hide
- *
- * Makes navigation bar transparent (but not the status bar).
- */
- public static final int NAVIGATION_BAR_TRANSPARENT = 0x00008000;
-
- /**
- * @hide
- *
- * Makes status bar transparent (but not the navigation bar).
- */
- public static final int STATUS_BAR_TRANSPARENT = 0x00000008;
-
- /**
- * @hide
- *
- * Makes both status bar and navigation bar transparent.
- */
- public static final int SYSTEM_UI_TRANSPARENT = NAVIGATION_BAR_TRANSPARENT
- | STATUS_BAR_TRANSPARENT;
-
- /**
- * @hide
*/
public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00003FF7;
@@ -4302,31 +4219,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
name = "STATUS_BAR_DISABLE_RECENT"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_SEARCH,
equals = STATUS_BAR_DISABLE_SEARCH,
- name = "STATUS_BAR_DISABLE_SEARCH"),
- @ViewDebug.FlagToString(mask = STATUS_BAR_TRANSIENT,
- equals = STATUS_BAR_TRANSIENT,
- name = "STATUS_BAR_TRANSIENT"),
- @ViewDebug.FlagToString(mask = NAVIGATION_BAR_TRANSIENT,
- equals = NAVIGATION_BAR_TRANSIENT,
- name = "NAVIGATION_BAR_TRANSIENT"),
- @ViewDebug.FlagToString(mask = STATUS_BAR_UNHIDE,
- equals = STATUS_BAR_UNHIDE,
- name = "STATUS_BAR_UNHIDE"),
- @ViewDebug.FlagToString(mask = NAVIGATION_BAR_UNHIDE,
- equals = NAVIGATION_BAR_UNHIDE,
- name = "NAVIGATION_BAR_UNHIDE"),
- @ViewDebug.FlagToString(mask = STATUS_BAR_TRANSLUCENT,
- equals = STATUS_BAR_TRANSLUCENT,
- name = "STATUS_BAR_TRANSLUCENT"),
- @ViewDebug.FlagToString(mask = NAVIGATION_BAR_TRANSLUCENT,
- equals = NAVIGATION_BAR_TRANSLUCENT,
- name = "NAVIGATION_BAR_TRANSLUCENT"),
- @ViewDebug.FlagToString(mask = NAVIGATION_BAR_TRANSPARENT,
- equals = NAVIGATION_BAR_TRANSPARENT,
- name = "NAVIGATION_BAR_TRANSPARENT"),
- @ViewDebug.FlagToString(mask = STATUS_BAR_TRANSPARENT,
- equals = STATUS_BAR_TRANSPARENT,
- name = "STATUS_BAR_TRANSPARENT")
+ name = "STATUS_BAR_DISABLE_SEARCH")
}, formatToHexString = true)
@SystemUiVisibility
int mSystemUiVisibility;
@@ -4355,14 +4248,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
STATUS_BAR_DISABLE_CLOCK,
STATUS_BAR_DISABLE_RECENT,
STATUS_BAR_DISABLE_SEARCH,
- STATUS_BAR_TRANSIENT,
- NAVIGATION_BAR_TRANSIENT,
- STATUS_BAR_UNHIDE,
- NAVIGATION_BAR_UNHIDE,
- STATUS_BAR_TRANSLUCENT,
- NAVIGATION_BAR_TRANSLUCENT,
- NAVIGATION_BAR_TRANSPARENT,
- STATUS_BAR_TRANSPARENT,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SystemUiVisibility {}
@@ -5358,6 +5243,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@InputSourceClass
int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE;
+ @Nullable
+ private OnReceiveContentCallback<? extends View> mOnReceiveContentCallback;
+
/**
* Simple constructor to use when creating a view from code.
*
@@ -9113,6 +9001,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Returns the callback used for handling insertion of content into this view. See
+ * {@link #setOnReceiveContentCallback} for more info.
+ *
+ * @return The callback for handling insertion of content. Returns null if no callback has been
+ * {@link #setOnReceiveContentCallback set}.
+ */
+ @Nullable
+ public OnReceiveContentCallback<? extends View> getOnReceiveContentCallback() {
+ return mOnReceiveContentCallback;
+ }
+
+ /**
+ * Sets the callback to handle insertion of content into this view.
+ *
+ * <p>Depending on the view, this callback may be invoked for scenarios such as content
+ * insertion from the IME, Autofill, etc.
+ *
+ * <p>The callback will only be invoked if the MIME type of the content is
+ * {@link OnReceiveContentCallback#getSupportedMimeTypes declared as supported} by the callback.
+ * If the content type is not supported by the callback, the default platform handling will be
+ * executed instead.
+ *
+ * @param callback The callback to use. This can be null to reset to the default behavior.
+ */
+ public void setOnReceiveContentCallback(
+ @Nullable OnReceiveContentCallback<? extends View> callback) {
+ mOnReceiveContentCallback = callback;
+ }
+
+ /**
* Automatically fills the content of this view with the {@code value}.
*
* <p>Views support the Autofill Framework mainly by:
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b1b77edad07f..778ebb056c8f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -33,6 +33,23 @@ import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewRootImplProto.ADDED;
+import static android.view.ViewRootImplProto.APP_VISIBLE;
+import static android.view.ViewRootImplProto.CUR_SCROLL_Y;
+import static android.view.ViewRootImplProto.DISPLAY_ID;
+import static android.view.ViewRootImplProto.HEIGHT;
+import static android.view.ViewRootImplProto.IS_ANIMATING;
+import static android.view.ViewRootImplProto.IS_DRAWING;
+import static android.view.ViewRootImplProto.LAST_WINDOW_INSETS;
+import static android.view.ViewRootImplProto.PENDING_DISPLAY_CUTOUT;
+import static android.view.ViewRootImplProto.REMOVED;
+import static android.view.ViewRootImplProto.SCROLL_Y;
+import static android.view.ViewRootImplProto.SOFT_INPUT_MODE;
+import static android.view.ViewRootImplProto.VIEW;
+import static android.view.ViewRootImplProto.VISIBLE_RECT;
+import static android.view.ViewRootImplProto.WIDTH;
+import static android.view.ViewRootImplProto.WINDOW_ATTRIBUTES;
+import static android.view.ViewRootImplProto.WIN_FRAME;
import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -61,6 +78,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.IME_FOCUS_CONTROLLER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.INSETS_CONTROLLER;
import android.Manifest;
import android.animation.LayoutTransition;
@@ -127,6 +146,8 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TypedValue;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.InputDevice.InputSourceClass;
import android.view.InsetsState.InternalInsetsType;
import android.view.Surface.OutOfResourcesException;
@@ -164,6 +185,7 @@ import android.window.ClientWindowFrames;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
import com.android.internal.policy.DecorView;
@@ -182,6 +204,7 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
@@ -339,8 +362,6 @@ public final class ViewRootImpl implements ViewParent,
final int mTargetSdkVersion;
- int mSeq;
-
@UnsupportedAppUsage
View mView;
@@ -648,7 +669,6 @@ public final class ViewRootImpl implements ViewParent,
private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
static final class SystemUiVisibilityInfo {
- int seq;
int globalVisibility;
int localValue;
int localChanges;
@@ -995,7 +1015,7 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
adjustLayoutParamsForCompatibility(mWindowAttributes);
- res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
+ res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrames.frame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
@@ -7415,7 +7435,7 @@ public final class ViewRootImpl implements ViewParent,
frameNumber = mSurface.getNextFrameNumber();
}
- int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
+ int relayoutResult = mWindowSession.relayout(mWindow, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
@@ -7545,6 +7565,38 @@ public final class ViewRootImpl implements ViewParent,
mView.debug();
}
+ /**
+ * Export the state of {@link ViewRootImpl} and other relevant classes into a protocol buffer
+ * output stream.
+ *
+ * @param proto Stream to write the state to
+ * @param fieldId FieldId of ViewRootImpl as defined in the parent message
+ */
+ @GuardedBy("this")
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(VIEW, Objects.toString(mView));
+ proto.write(DISPLAY_ID, mDisplay.getDisplayId());
+ proto.write(APP_VISIBLE, mAppVisible);
+ proto.write(HEIGHT, mHeight);
+ proto.write(WIDTH, mWidth);
+ proto.write(IS_ANIMATING, mIsAnimating);
+ mVisRect.dumpDebug(proto, VISIBLE_RECT);
+ proto.write(IS_DRAWING, mIsDrawing);
+ proto.write(ADDED, mAdded);
+ mWinFrame.dumpDebug(proto, WIN_FRAME);
+ mPendingDisplayCutout.get().dumpDebug(proto, PENDING_DISPLAY_CUTOUT);
+ proto.write(LAST_WINDOW_INSETS, Objects.toString(mLastWindowInsets));
+ proto.write(SOFT_INPUT_MODE, InputMethodDebug.softInputModeToString(mSoftInputMode));
+ proto.write(SCROLL_Y, mScrollY);
+ proto.write(CUR_SCROLL_Y, mCurScrollY);
+ proto.write(REMOVED, mRemoved);
+ mWindowAttributes.dumpDebug(proto, WINDOW_ATTRIBUTES);
+ proto.end(token);
+ mInsetsController.dumpDebug(proto, INSETS_CONTROLLER);
+ mImeFocusController.dumpDebug(proto, IME_FOCUS_CONTROLLER);
+ }
+
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
String innerPrefix = prefix + " ";
writer.println(prefix + "ViewRoot:");
@@ -8485,17 +8537,6 @@ public final class ViewRootImpl implements ViewParent,
mHandler.sendMessage(msg);
}
- // TODO(118118435): Remove this after migration
- public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
- int localValue, int localChanges) {
- SystemUiVisibilityInfo args = new SystemUiVisibilityInfo();
- args.seq = seq;
- args.globalVisibility = globalVisibility;
- args.localValue = localValue;
- args.localChanges = localChanges;
- mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args));
- }
-
public void dispatchCheckFocus() {
if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) {
// This will result in a call to checkFocus() below.
@@ -9119,6 +9160,9 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void showInsets(@InsetsType int types, boolean fromIme) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.showInsets(types, fromIme);
@@ -9127,6 +9171,9 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void hideInsets(@InsetsType int types, boolean fromIme) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.hideInsets(types, fromIme);
@@ -9254,16 +9301,6 @@ public final class ViewRootImpl implements ViewParent,
}
@Override
- public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
- int localValue, int localChanges) {
- final ViewRootImpl viewAncestor = mViewAncestor.get();
- if (viewAncestor != null) {
- viewAncestor.dispatchSystemUiVisibilityChanged(seq, globalVisibility,
- localValue, localChanges);
- }
- }
-
- @Override
public void dispatchWindowShown() {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0d62da6bc8e3..e96e98b437a1 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3994,4 +3994,15 @@ public interface WindowManager extends ViewManager {
}
}
}
+
+ /**
+ * Holds the WM lock for the specified amount of milliseconds.
+ * Intended for use by the tests that need to imitate lock contention.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
+ default void holdLock(int durationMs) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index f57ee65948c0..59e022645544 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -281,4 +281,13 @@ public final class WindowManagerImpl implements WindowManager {
throw e.rethrowFromSystemServer();
}
}
+
+ @Override
+ public void holdLock(int durationMs) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().holdLock(durationMs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index b70cb015a275..dbd8184e57bb 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -129,7 +129,7 @@ public class WindowlessWindowManager implements IWindowSession {
* IWindowSession implementation.
*/
@Override
- public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
@@ -165,18 +165,18 @@ public class WindowlessWindowManager implements IWindowSession {
* IWindowSession implementation. Currently this class doesn't need to support for multi-user.
*/
@Override
- public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
- return addToDisplay(window, seq, attrs, viewVisibility, displayId,
+ return addToDisplay(window, attrs, viewVisibility, displayId,
outFrame, outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
outInsetsState, outActiveControls);
}
@Override
- public int addToDisplayWithoutInputChannel(android.view.IWindow window, int seq,
+ public int addToDisplayWithoutInputChannel(android.view.IWindow window,
android.view.WindowManager.LayoutParams attrs, int viewVisibility, int layerStackId,
android.graphics.Rect outContentInsets, android.graphics.Rect outStableInsets,
android.view.InsetsState insetsState) {
@@ -223,7 +223,7 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
- public int relayout(IWindow window, int seq, WindowManager.LayoutParams inAttrs,
+ public int relayout(IWindow window, WindowManager.LayoutParams inAttrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index eef27262c699..73636f81369f 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -16,7 +16,10 @@
package android.view.inputmethod;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD;
+
import android.annotation.CallSuper;
+import android.content.ClipData;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
@@ -34,6 +37,7 @@ import android.util.Log;
import android.util.LogPrinter;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import android.view.OnReceiveContentCallback;
import android.view.View;
class ComposingText implements NoCopySpan {
@@ -870,9 +874,41 @@ public class BaseInputConnection implements InputConnection {
}
/**
- * The default implementation does nothing.
+ * Default implementation which invokes the target view's {@link OnReceiveContentCallback} if
+ * it is {@link View#setOnReceiveContentCallback set} and supports the MIME type of the given
+ * content; otherwise, simply returns false.
*/
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
- return false;
+ @SuppressWarnings("unchecked") final OnReceiveContentCallback<View> receiver =
+ (OnReceiveContentCallback<View>) mTargetView.getOnReceiveContentCallback();
+ if (receiver == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Can't insert content from IME; no callback");
+ }
+ return false;
+ }
+ if (!receiver.supports(mTargetView, inputContentInfo.getDescription())) {
+ if (DEBUG) {
+ Log.d(TAG, "Can't insert content from IME; callback doesn't support MIME type: "
+ + inputContentInfo.getDescription());
+ }
+ return false;
+ }
+ if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
+ try {
+ inputContentInfo.requestPermission();
+ } catch (Exception e) {
+ Log.w(TAG, "Can't insert content from IME; requestPermission() failed", e);
+ return false;
+ }
+ }
+ final ClipData clip = new ClipData(inputContentInfo.getDescription(),
+ new ClipData.Item(inputContentInfo.getContentUri()));
+ final OnReceiveContentCallback.Payload payload =
+ new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_INPUT_METHOD)
+ .setLinkUri(inputContentInfo.getLinkUri())
+ .setExtras(opts)
+ .build();
+ return receiver.onReceiveContent(mTargetView, payload);
}
}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 104bc4347c29..7dbf69369996 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -17,6 +17,12 @@
package android.view.inputmethod;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.view.inputmethod.EditorInfoProto.FIELD_ID;
+import static android.view.inputmethod.EditorInfoProto.IME_OPTIONS;
+import static android.view.inputmethod.EditorInfoProto.INPUT_TYPE;
+import static android.view.inputmethod.EditorInfoProto.PACKAGE_NAME;
+import static android.view.inputmethod.EditorInfoProto.PRIVATE_IME_OPTIONS;
+import static android.view.inputmethod.EditorInfoProto.TARGET_INPUT_METHOD_USER_ID;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -32,6 +38,7 @@ import android.text.InputType;
import android.text.ParcelableSpan;
import android.text.TextUtils;
import android.util.Printer;
+import android.util.proto.ProtoOutputStream;
import android.view.View;
import android.view.autofill.AutofillId;
@@ -795,6 +802,26 @@ public class EditorInfo implements InputType, Parcelable {
}
/**
+ * Export the state of {@link EditorInfo} into a protocol buffer output stream.
+ *
+ * @param proto Stream to write the state to
+ * @param fieldId FieldId of ViewRootImpl as defined in the parent message
+ * @hide
+ */
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(INPUT_TYPE, inputType);
+ proto.write(IME_OPTIONS, imeOptions);
+ proto.write(PRIVATE_IME_OPTIONS, privateImeOptions);
+ proto.write(PACKAGE_NAME, packageName);
+ proto.write(FIELD_ID, this.fieldId);
+ if (targetInputMethodUser != null) {
+ proto.write(TARGET_INPUT_METHOD_USER_ID, targetInputMethodUser.getIdentifier());
+ }
+ proto.end(token);
+ }
+
+ /**
* Write debug output of this object.
*/
public void dump(Printer pw, String prefix) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a3c95a916669..b8f04159faa9 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -18,6 +18,17 @@ package android.view.inputmethod;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static android.util.imetracing.ImeTracing.PROTO_ARG;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.DISPLAY_ID;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.EDITOR_INFO;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.INPUT_METHOD_MANAGER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.VIEW_ROOT_IMPL;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientsProto.CLIENT;
+import static android.view.inputmethod.InputMethodManagerProto.ACTIVE;
+import static android.view.inputmethod.InputMethodManagerProto.CUR_ID;
+import static android.view.inputmethod.InputMethodManagerProto.FULLSCREEN_MODE;
+import static android.view.inputmethod.InputMethodManagerProto.SERVED_CONNECTING;
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;
@@ -62,6 +73,8 @@ import android.util.Pools.SimplePool;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.SparseArray;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.ImeFocusController;
import android.view.ImeInsetsSourceConsumer;
@@ -564,6 +577,7 @@ public final class InputMethodManager {
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags) {
final View servedView;
+ ImeTracing.getInstance().triggerDump();
synchronized (mH) {
mCurrentTextBoxAttribute = null;
mCompletions = null;
@@ -610,7 +624,8 @@ public final class InputMethodManager {
@Override
public void startInputAsyncOnWindowFocusGain(View focusedView,
@SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus) {
- final int startInputFlags = getStartInputFlags(focusedView, 0);
+ int startInputFlags = getStartInputFlags(focusedView, 0);
+ startInputFlags |= StartInputFlags.WINDOW_GAINED_FOCUS;
final ImeFocusController controller = getFocusController();
if (controller == null) {
@@ -1083,6 +1098,11 @@ public final class InputMethodManager {
mH.obtainMessage(MSG_UPDATE_ACTIVITY_VIEW_TO_SCREEN_MATRIX, bindSequence, 0,
matrixValues).sendToTarget();
}
+
+ @Override
+ public void setImeTraceEnabled(boolean enabled) {
+ ImeTracing.getInstance().setEnabled(enabled);
+ }
};
final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
@@ -1651,6 +1671,7 @@ public final class InputMethodManager {
* {@link #RESULT_HIDDEN}.
*/
public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
+ ImeTracing.getInstance().triggerDump();
// Re-dispatch if there is a context mismatch.
final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
if (fallbackImm != null) {
@@ -1756,6 +1777,7 @@ public final class InputMethodManager {
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
ResultReceiver resultReceiver) {
+ ImeTracing.getInstance().triggerDump();
checkFocus();
synchronized (mH) {
final View servedView = getServedViewLocked();
@@ -3107,6 +3129,10 @@ public final class InputMethodManager {
}
void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ if (processDump(fd, args)) {
+ return;
+ }
+
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method client state for " + this + ":");
@@ -3201,4 +3227,74 @@ public final class InputMethodManager {
return sb.toString();
}
+
+ /**
+ * Checks the args to see if a proto-based ime dump was requested and writes the client side
+ * ime dump to the given {@link FileDescriptor}.
+ *
+ * @return {@code true} if a proto-based ime dump was requested.
+ */
+ private boolean processDump(final FileDescriptor fd, final String[] args) {
+ if (args == null) {
+ return false;
+ }
+
+ for (String arg : args) {
+ if (arg.equals(PROTO_ARG)) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ dumpProto(proto);
+ proto.flush();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Write the proto dump for all displays associated with this client.
+ *
+ * @param proto The proto stream to which the dumps are written.
+ * @hide
+ */
+ public static void dumpProto(ProtoOutputStream proto) {
+ for (int i = sInstanceMap.size() - 1; i >= 0; i--) {
+ InputMethodManager imm = sInstanceMap.valueAt(i);
+ imm.dumpDebug(proto);
+ }
+ }
+
+ /**
+ * Write the proto dump of various client side components to the provided
+ * {@link ProtoOutputStream}.
+ *
+ * @param proto The proto stream to which the dumps are written.
+ * @hide
+ */
+ @GuardedBy("mH")
+ public void dumpDebug(ProtoOutputStream proto) {
+ if (mCurMethod == null) {
+ return;
+ }
+
+ final long clientDumpToken = proto.start(CLIENT);
+ proto.write(DISPLAY_ID, mDisplayId);
+ final long token = proto.start(INPUT_METHOD_MANAGER);
+ synchronized (mH) {
+ proto.write(CUR_ID, mCurId);
+ proto.write(FULLSCREEN_MODE, mFullscreenMode);
+ proto.write(ACTIVE, mActive);
+ proto.write(SERVED_CONNECTING, mServedConnecting);
+ proto.end(token);
+ if (mCurRootView != null) {
+ mCurRootView.dumpDebug(proto, VIEW_ROOT_IMPL);
+ }
+ if (mCurrentTextBoxAttribute != null) {
+ mCurrentTextBoxAttribute.dumpDebug(proto, EDITOR_INFO);
+ }
+ if (mImeInsetsConsumer != null) {
+ mImeInsetsConsumer.dumpDebug(proto, IME_INSETS_SOURCE_CONSUMER);
+ }
+ }
+ proto.end(clientDumpToken);
+ }
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 60f8bb7ebe6c..fa195211fb54 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP;
+
import android.R;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -96,6 +98,7 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.OnReceiveContentCallback;
import android.view.SubMenu;
import android.view.View;
import android.view.View.DragShadowBuilder;
@@ -2851,9 +2854,11 @@ public class Editor {
try {
final int originalLength = mTextView.getText().length();
Selection.setSelection((Spannable) mTextView.getText(), offset);
- ClipData clip = event.getClipData();
- mTextView.getRichContentReceiver().onReceive(mTextView, clip,
- RichContentReceiver.SOURCE_DRAG_AND_DROP, 0);
+ final ClipData clip = event.getClipData();
+ final OnReceiveContentCallback.Payload payload =
+ new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_DRAG_AND_DROP)
+ .build();
+ mTextView.onReceiveContent(payload);
if (dragDropIntoItself) {
deleteSourceAfterLocalDrop(dragLocalState, offset, originalLength);
}
diff --git a/core/java/android/widget/RichContentReceiver.java b/core/java/android/widget/RichContentReceiver.java
deleted file mode 100644
index 80a05622aa67..000000000000
--- a/core/java/android/widget/RichContentReceiver.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.widget;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.SuppressLint;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.view.View;
-import android.view.inputmethod.InputConnection;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Set;
-
-/**
- * Callback for apps to implement handling for insertion of rich content. "Rich content" here refers
- * to both text and non-text content: plain text, styled text, HTML, images, videos, audio files,
- * etc.
- *
- * <p>This callback can be attached to different types of UI components. For editable
- * {@link TextView} components, implementations should typically wrap
- * {@link TextView#DEFAULT_RICH_CONTENT_RECEIVER}.
- *
- * <p>Example implementation:<br>
- * <pre class="prettyprint">
- * public class MyRichContentReceiver implements RichContentReceiver&lt;TextView&gt; {
- *
- * private static final Set&lt;String&gt; SUPPORTED_MIME_TYPES = Collections.unmodifiableSet(
- * Set.of("text/*", "image/gif", "image/png", "image/jpg"));
- *
- * &#64;NonNull
- * &#64;Override
- * public Set&lt;String&gt; getSupportedMimeTypes() {
- * return SUPPORTED_MIME_TYPES;
- * }
- *
- * &#64;Override
- * public boolean onReceive(@NonNull TextView textView, @NonNull ClipData clip,
- * int source, int flags) {
- * if (clip.getDescription().hasMimeType("image/*")) {
- * return receiveImage(textView, clip);
- * }
- * return TextView.DEFAULT_RICH_CONTENT_RECEIVER.onReceive(textView, clip, source);
- * }
- *
- * private boolean receiveImage(@NonNull TextView textView, @NonNull ClipData clip) {
- * // ... app-specific logic to handle the content URI in the clip ...
- * }
- * }
- * </pre>
- *
- * @param <T> The type of {@link View} with which this receiver can be associated.
- */
-@SuppressLint("CallbackMethodName")
-public interface RichContentReceiver<T extends View> {
- /**
- * Specifies the UI through which content is being inserted.
- *
- * @hide
- */
- @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD,
- SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT})
- @Retention(RetentionPolicy.SOURCE)
- @interface Source {}
-
- /**
- * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or
- * "Paste as plain text" action in the insertion/selection menu).
- */
- int SOURCE_CLIPBOARD = 0;
-
- /**
- * Specifies that the operation was triggered from the soft keyboard (also known as input method
- * editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard for more
- * info.
- */
- int SOURCE_INPUT_METHOD = 1;
-
- /**
- * Specifies that the operation was triggered by the drag/drop framework. See
- * https://developer.android.com/guide/topics/ui/drag-drop for more info.
- */
- int SOURCE_DRAG_AND_DROP = 2;
-
- /**
- * Specifies that the operation was triggered by the autofill framework. See
- * https://developer.android.com/guide/topics/text/autofill for more info.
- */
- int SOURCE_AUTOFILL = 3;
-
- /**
- * Specifies that the operation was triggered by a result from a
- * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection menu.
- */
- int SOURCE_PROCESS_TEXT = 4;
-
- /**
- * Flags to configure the insertion behavior.
- *
- * @hide
- */
- @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT})
- @Retention(RetentionPolicy.SOURCE)
- @interface Flags {}
-
- /**
- * Flag for {@link #onReceive} requesting that the content should be converted to plain text
- * prior to inserting.
- */
- int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0;
-
- /**
- * Insert the given clip.
- *
- * <p>For editable {@link TextView} components, this function will be invoked for the
- * following scenarios:
- * <ol>
- * <li>Paste from the clipboard (e.g. "Paste" or "Paste as plain text" action in the
- * insertion/selection menu)
- * <li>Content insertion from the keyboard ({@link InputConnection#commitContent})
- * <li>Drag and drop ({@link View#onDragEvent})
- * <li>Autofill, when the type for the field is
- * {@link android.view.View.AutofillType#AUTOFILL_TYPE_RICH_CONTENT}
- * </ol>
- *
- * <p>For text, if the view has a selection, the selection should be overwritten by the
- * clip; if there's no selection, this method should insert the content at the current
- * cursor position.
- *
- * <p>For rich content (e.g. an image), this function may insert the content inline, or it may
- * add the content as an attachment (could potentially go into a completely separate view).
- *
- * <p>This function may be invoked with a clip whose MIME type is not in the list of supported
- * types returned by {@link #getSupportedMimeTypes()}. This provides the opportunity to
- * implement custom fallback logic if desired.
- *
- * @param view The view where the content insertion was requested.
- * @param clip The clip to insert.
- * @param source The trigger of the operation.
- * @param flags Optional flags to configure the insertion behavior. Use 0 for default
- * behavior. See {@code FLAG_} constants on this interface for other options.
- * @return Returns true if the clip was inserted.
- */
- boolean onReceive(@NonNull T view, @NonNull ClipData clip, @Source int source, int flags);
-
- /**
- * Returns the MIME types that can be handled by this callback.
- *
- * <p>Different platform features (e.g. pasting from the clipboard, inserting stickers from the
- * keyboard, etc) may use this function to conditionally alter their behavior. For example, the
- * keyboard may choose to hide its UI for inserting GIFs if the input field that has focus has
- * a {@link RichContentReceiver} set and the MIME types returned from this function don't
- * include "image/gif".
- *
- * @return An immutable set with the MIME types supported by this callback. The returned
- * MIME types may contain wildcards such as "text/*", "image/*", etc.
- */
- @NonNull
- Set<String> getSupportedMimeTypes();
-
- /**
- * Returns true if the MIME type of the given clip is {@link #getSupportedMimeTypes supported}
- * by this receiver.
- *
- * @hide
- */
- default boolean supports(@NonNull ClipDescription description) {
- for (String supportedMimeType : getSupportedMimeTypes()) {
- if (description.hasMimeType(supportedMimeType)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns true if this receiver {@link #getSupportedMimeTypes supports} non-text content, such
- * as images.
- *
- * @hide
- */
- default boolean supportsNonTextContent() {
- for (String supportedMimeType : getSupportedMimeTypes()) {
- if (!supportedMimeType.startsWith("text/")) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns the symbolic name of the given source.
- *
- * @hide
- */
- static String sourceToString(@Source int source) {
- switch (source) {
- case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD";
- case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD";
- case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP";
- case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL";
- case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT";
- }
- return String.valueOf(source);
- }
-
- /**
- * Returns the symbolic names of the set flags or {@code "0"} if no flags are set.
- *
- * @hide
- */
- static String flagsToString(@Flags int flags) {
- if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) {
- return "FLAG_CONVERT_TO_PLAIN_TEXT";
- }
- return String.valueOf(flags);
- }
-}
diff --git a/core/java/android/widget/TEST_MAPPING b/core/java/android/widget/TEST_MAPPING
index b5beac99d4e2..49c409368448 100644
--- a/core/java/android/widget/TEST_MAPPING
+++ b/core/java/android/widget/TEST_MAPPING
@@ -22,7 +22,7 @@
"name": "CtsAutoFillServiceTestCases",
"options": [
{
- "include-filter": "android.autofillservice.cts.LoginActivityTest"
+ "include-filter": "android.autofillservice.cts.dropdown.LoginActivityTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
@@ -36,7 +36,7 @@
"name": "CtsAutoFillServiceTestCases",
"options": [
{
- "include-filter": "android.autofillservice.cts.CheckoutActivityTest"
+ "include-filter": "android.autofillservice.cts.dropdown.CheckoutActivityTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6f14dfb89e6b..52a3f4145e7e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,12 +17,15 @@
package android.widget;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.view.OnReceiveContentCallback.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_CLIPBOARD;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_PROCESS_TEXT;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
-import static android.widget.RichContentReceiver.SOURCE_PROCESS_TEXT;
import android.R;
import android.annotation.CallSuper;
@@ -39,6 +42,7 @@ import android.annotation.RequiresPermission;
import android.annotation.Size;
import android.annotation.StringRes;
import android.annotation.StyleRes;
+import android.annotation.TestApi;
import android.annotation.XmlRes;
import android.app.Activity;
import android.app.PendingIntent;
@@ -149,6 +153,7 @@ import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.OnReceiveContentCallback;
import android.view.PointerIcon;
import android.view.View;
import android.view.ViewConfiguration;
@@ -426,7 +431,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* @hide
*/
- static final int PROCESS_TEXT_REQUEST_CODE = 100;
+ @TestApi
+ public static final int PROCESS_TEXT_REQUEST_CODE = 100;
/**
* Return code of {@link #doKeyDown}.
@@ -735,6 +741,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private boolean mLocalesChanged = false;
private int mTextSizeUnit = -1;
+ // True if force bold text feature is enabled. This feature makes all text bolder.
+ private boolean mForceBoldTextEnabled;
+ private Typeface mOriginalTypeface;
+
// True if setKeyListener() has been explicitly called
private boolean mListenerChanged = false;
// True if internationalized input should be used for numbers and date and time.
@@ -882,12 +892,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* The default content insertion callback used by {@link TextView}. See
- * {@link #setRichContentReceiver} for more info.
+ * {@link #setOnReceiveContentCallback} for more info.
*/
- public static final @NonNull RichContentReceiver<TextView> DEFAULT_RICH_CONTENT_RECEIVER =
- TextViewRichContentReceiver.INSTANCE;
-
- private RichContentReceiver<TextView> mRichContentReceiver = DEFAULT_RICH_CONTENT_RECEIVER;
+ private static final TextViewOnReceiveContentCallback DEFAULT_ON_RECEIVE_CONTENT_CALLBACK =
+ new TextViewOnReceiveContentCallback();
private static final int DEVICE_PROVISIONED_UNKNOWN = 0;
private static final int DEVICE_PROVISIONED_NO = 1;
@@ -1645,6 +1653,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
attributes.mTypefaceIndex = MONOSPACE;
}
+ mForceBoldTextEnabled = getContext().getResources().getConfiguration().forceBoldText
+ == Configuration.FORCE_BOLD_TEXT_YES;
applyTextAppearance(attributes);
if (isPassword) {
@@ -2138,15 +2148,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* @hide
*/
+ @TestApi
@Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (requestCode == PROCESS_TEXT_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT);
if (result != null) {
if (isTextEditable()) {
ClipData clip = ClipData.newPlainText("", result);
- mRichContentReceiver.onReceive(this, clip, SOURCE_PROCESS_TEXT, 0);
+ OnReceiveContentCallback.Payload payload =
+ new OnReceiveContentCallback.Payload.Builder(
+ clip, SOURCE_PROCESS_TEXT)
+ .build();
+ onReceiveContent(payload);
if (mEditor != null) {
mEditor.refreshTextActionMode();
}
@@ -4267,6 +4282,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
invalidate();
}
}
+ if (newConfig.forceBoldText == Configuration.FORCE_BOLD_TEXT_YES) {
+ mForceBoldTextEnabled = true;
+ setTypeface(getTypeface());
+ } else if (newConfig.forceBoldText == Configuration.FORCE_BOLD_TEXT_NO
+ || newConfig.forceBoldText == Configuration.FORCE_BOLD_TEXT_UNDEFINED) {
+ mForceBoldTextEnabled = false;
+ setTypeface(getTypeface());
+ }
}
/**
@@ -4418,6 +4441,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_textStyle
*/
public void setTypeface(@Nullable Typeface tf) {
+ mOriginalTypeface = tf;
+ if (mForceBoldTextEnabled) {
+ int newWeight = tf != null ? tf.getWeight() + 300 : 400;
+ newWeight = Math.min(newWeight, 1000);
+ int typefaceStyle = tf != null ? tf.getStyle() : 0;
+ boolean italic = (typefaceStyle & Typeface.ITALIC) != 0;
+ tf = Typeface.create(tf, newWeight, italic);
+ }
if (mTextPaint.getTypeface() != tf) {
mTextPaint.setTypeface(tf);
@@ -4441,7 +4472,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
@InspectableProperty
public Typeface getTypeface() {
- return mTextPaint.getTypeface();
+ return mOriginalTypeface;
}
/**
@@ -8722,9 +8753,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
outAttrs.initialSelEnd = getSelectionEnd();
outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType());
outAttrs.setInitialSurroundingText(mText);
- int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
- if (targetSdkVersion > Build.VERSION_CODES.R) {
- outAttrs.contentMimeTypes = mRichContentReceiver.getSupportedMimeTypes()
+ // If a custom `OnReceiveContentCallback` is set, pass its supported MIME types.
+ OnReceiveContentCallback<TextView> receiver = getOnReceiveContentCallback();
+ if (receiver != null) {
+ outAttrs.contentMimeTypes = receiver.getSupportedMimeTypes(this)
.toArray(new String[0]);
}
return ic;
@@ -11827,7 +11859,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Log.w(LOG_TAG, "cannot autofill non-editable TextView: " + this);
return;
}
- ClipData clip;
+ final ClipData clip;
if (value.isRichContent()) {
clip = value.getRichContentValue();
} else if (value.isText()) {
@@ -11837,22 +11869,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
+ " cannot be autofilled into " + this);
return;
}
- mRichContentReceiver.onReceive(this, clip, RichContentReceiver.SOURCE_AUTOFILL, 0);
+ final OnReceiveContentCallback.Payload payload =
+ new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL).build();
+ onReceiveContent(payload);
}
@Override
public @AutofillType int getAutofillType() {
- if (!isTextEditable()) {
- return AUTOFILL_TYPE_NONE;
- }
- final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
- if (targetSdkVersion <= Build.VERSION_CODES.R) {
- return AUTOFILL_TYPE_TEXT;
- }
- // TODO(b/147301047): Update autofill framework code to check the target SDK of the autofill
- // provider and force the type AUTOFILL_TYPE_TEXT for providers that target older SDKs.
- return mRichContentReceiver.supportsNonTextContent() ? AUTOFILL_TYPE_RICH_CONTENT
- : AUTOFILL_TYPE_TEXT;
+ return isTextEditable() ? AUTOFILL_TYPE_TEXT : AUTOFILL_TYPE_NONE;
}
/**
@@ -12913,8 +12937,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (clip == null) {
return;
}
- int flags = withFormatting ? 0 : RichContentReceiver.FLAG_CONVERT_TO_PLAIN_TEXT;
- mRichContentReceiver.onReceive(this, clip, RichContentReceiver.SOURCE_CLIPBOARD, flags);
+ final OnReceiveContentCallback.Payload payload =
+ new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_CLIPBOARD)
+ .setFlags(withFormatting ? 0 : FLAG_CONVERT_TO_PLAIN_TEXT)
+ .build();
+ onReceiveContent(payload);
sLastCutCopyOrTextChangedTime = 0;
}
@@ -13697,43 +13724,58 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
- * Returns the callback that handles insertion of content into this view (e.g. pasting from
- * the clipboard). See {@link #setRichContentReceiver} for more info.
+ * Returns the callback used for handling insertion of content into this view. See
+ * {@link #setOnReceiveContentCallback} for more info.
*
- * @return The callback that this view is using to handle insertion of content. Returns
- * {@link #DEFAULT_RICH_CONTENT_RECEIVER} if no custom callback has been
- * {@link #setRichContentReceiver set}.
+ * @return The callback for handling insertion of content. Returns null if no callback has been
+ * {@link #setOnReceiveContentCallback set}.
*/
- @NonNull
- public RichContentReceiver<TextView> getRichContentReceiver() {
- return mRichContentReceiver;
+ @SuppressWarnings("unchecked")
+ @Nullable
+ @Override
+ public OnReceiveContentCallback<TextView> getOnReceiveContentCallback() {
+ return (OnReceiveContentCallback<TextView>) super.getOnReceiveContentCallback();
}
/**
* Sets the callback to handle insertion of content into this view.
*
- * <p>"Content" and "rich content" here refers to both text and non-text: plain text, styled
- * text, HTML, images, videos, audio files, etc.
- *
- * <p>The callback configured here should typically wrap {@link #DEFAULT_RICH_CONTENT_RECEIVER}
- * to provide consistent behavior for text content.
- *
* <p>This callback will be invoked for the following scenarios:
* <ol>
* <li>Paste from the clipboard (e.g. "Paste" or "Paste as plain text" action in the
* insertion/selection menu)
- * <li>Content insertion from the keyboard ({@link InputConnection#commitContent})
- * <li>Drag and drop ({@link View#onDragEvent})
- * <li>Autofill, when the type for the field is
- * {@link android.view.View.AutofillType#AUTOFILL_TYPE_RICH_CONTENT}
+ * <li>Content insertion from the keyboard (from {@link InputConnection#commitContent})
+ * <li>Drag and drop (drop events from {@link #onDragEvent(DragEvent)})
+ * <li>Autofill (from {@link #autofill(AutofillValue)})
+ * <li>{@link Intent#ACTION_PROCESS_TEXT} replacement
* </ol>
*
- * @param receiver The callback to use. This can be {@link #DEFAULT_RICH_CONTENT_RECEIVER} to
- * reset to the default behavior.
+ * <p>The callback will only be invoked if the MIME type of the content is
+ * {@link OnReceiveContentCallback#getSupportedMimeTypes declared as supported} by the callback.
+ * If the content type is not supported by the callback, the default platform handling will be
+ * executed instead.
+ *
+ * @param callback The callback to use. This can be null to reset to the default behavior.
*/
- public void setRichContentReceiver(@NonNull RichContentReceiver<TextView> receiver) {
- mRichContentReceiver = Objects.requireNonNull(receiver,
- "RichContentReceiver should not be null.");
+ @Override
+ public void setOnReceiveContentCallback(
+ @Nullable OnReceiveContentCallback<? extends View> callback) {
+ super.setOnReceiveContentCallback(callback);
+ }
+
+ /**
+ * Handles the request to insert content using the configured callback or the default callback.
+ *
+ * @hide
+ */
+ void onReceiveContent(@NonNull OnReceiveContentCallback.Payload payload) {
+ OnReceiveContentCallback<TextView> receiver = getOnReceiveContentCallback();
+ ClipDescription description = payload.getClip().getDescription();
+ if (receiver != null && receiver.supports(this, description)) {
+ receiver.onReceiveContent(this, payload);
+ } else {
+ DEFAULT_ON_RECEIVE_CONTENT_CALLBACK.onReceiveContent(this, payload);
+ }
}
private static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
diff --git a/core/java/android/widget/TextViewRichContentReceiver.java b/core/java/android/widget/TextViewOnReceiveContentCallback.java
index 4f2d95466997..35618cb3d2a5 100644
--- a/core/java/android/widget/TextViewRichContentReceiver.java
+++ b/core/java/android/widget/TextViewOnReceiveContentCallback.java
@@ -16,7 +16,12 @@
package android.widget;
+import static android.view.OnReceiveContentCallback.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP;
+
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.Context;
import android.text.Editable;
@@ -24,54 +29,45 @@ import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.util.Log;
+import android.view.OnReceiveContentCallback;
+import android.view.OnReceiveContentCallback.Payload.Flags;
+import android.view.OnReceiveContentCallback.Payload.Source;
import java.util.Collections;
import java.util.Set;
/**
- * Default implementation of {@link RichContentReceiver} for editable {@link TextView} components.
- * This class handles insertion of text (plain text, styled text, HTML, etc) but not images or other
- * rich content. Typically this class will be used as a delegate by custom implementations of
- * {@link RichContentReceiver}, to provide consistent behavior for insertion of text while
- * implementing custom behavior for insertion of other content (images, etc). See
- * {@link TextView#DEFAULT_RICH_CONTENT_RECEIVER}.
- *
- * @hide
+ * Default implementation of {@link android.view.OnReceiveContentCallback} for editable
+ * {@link TextView} components. This class handles insertion of text (plain text, styled text, HTML,
+ * etc) but not images or other content. This class can be used as a base class for an
+ * implementation of {@link android.view.OnReceiveContentCallback} for a {@link TextView}, to
+ * provide consistent behavior for insertion of text.
*/
-final class TextViewRichContentReceiver implements RichContentReceiver<TextView> {
- static final TextViewRichContentReceiver INSTANCE = new TextViewRichContentReceiver();
-
- private static final String LOG_TAG = "RichContentReceiver";
+public class TextViewOnReceiveContentCallback implements OnReceiveContentCallback<TextView> {
+ private static final String LOG_TAG = "OnReceiveContent";
private static final Set<String> MIME_TYPES_ALL_TEXT = Collections.singleton("text/*");
+ @SuppressLint("CallbackMethodName")
+ @NonNull
@Override
- public Set<String> getSupportedMimeTypes() {
+ public Set<String> getSupportedMimeTypes(@NonNull TextView view) {
return MIME_TYPES_ALL_TEXT;
}
@Override
- public boolean onReceive(@NonNull TextView textView, @NonNull ClipData clip,
- @Source int source, @Flags int flags) {
+ public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) {
if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
- StringBuilder sb = new StringBuilder("onReceive: clip=");
- if (clip.getDescription() == null) {
- sb.append("null");
- } else {
- clip.getDescription().toShortStringTypesOnly(sb);
- }
- sb.append(", source=").append(RichContentReceiver.sourceToString(source));
- sb.append(", flags=").append(RichContentReceiver.flagsToString(flags));
- Log.d(LOG_TAG, sb.toString());
+ Log.d(LOG_TAG, "onReceive:" + payload);
}
+ ClipData clip = payload.getClip();
+ @Source int source = payload.getSource();
+ @Flags int flags = payload.getFlags();
if (source == SOURCE_AUTOFILL) {
- return onReceiveForAutofill(textView, clip, flags);
+ return onReceiveForAutofill(view, clip, flags);
}
if (source == SOURCE_DRAG_AND_DROP) {
- return onReceiveForDragAndDrop(textView, clip, flags);
- }
- if (source == SOURCE_INPUT_METHOD && !supports(clip.getDescription())) {
- return false;
+ return onReceiveForDragAndDrop(view, clip, flags);
}
// The code here follows the original paste logic from TextView:
@@ -79,8 +75,8 @@ final class TextViewRichContentReceiver implements RichContentReceiver<TextView>
// In particular, multiple items within the given ClipData will trigger separate calls to
// replace/insert. This is to preserve the original behavior with respect to TextWatcher
// notifications fired from SpannableStringBuilder when replace/insert is called.
- final Editable editable = (Editable) textView.getText();
- final Context context = textView.getContext();
+ final Editable editable = (Editable) view.getText();
+ final Context context = view.getContext();
boolean didFirst = false;
for (int i = 0; i < clip.getItemCount(); i++) {
CharSequence itemText;
@@ -100,7 +96,7 @@ final class TextViewRichContentReceiver implements RichContentReceiver<TextView>
}
}
}
- return didFirst;
+ return true;
}
private static void replaceSelection(@NonNull Editable editable,
@@ -128,7 +124,7 @@ final class TextViewRichContentReceiver implements RichContentReceiver<TextView>
@NonNull ClipData clip, @Flags int flags) {
final CharSequence text = coerceToText(clip, textView.getContext(), flags);
if (text.length() == 0) {
- return false;
+ return true;
}
replaceSelection((Editable) textView.getText(), text);
return true;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 944f2ecdd647..61a625e40dcd 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1771,7 +1771,7 @@ public class ChooserActivity extends ResolverActivity implements
case ChooserListAdapter.TARGET_CALLER:
case ChooserListAdapter.TARGET_STANDARD:
cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET;
- value -= currentListAdapter.getSelectableServiceTargetCount();
+ value -= currentListAdapter.getSurfacedTargetInfo().size();
numCallerProvided = currentListAdapter.getCallerTargetCount();
getChooserActivityLogger().logShareTargetSelected(
SELECTION_TYPE_APP,
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 37f68233db53..3bcba75ec163 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -224,6 +224,8 @@ public final class InputMethodDebug {
return "HIDE_DOCKED_STACK_ATTACHED";
case SoftInputShowHideReason.HIDE_RECENTS_ANIMATION:
return "HIDE_RECENTS_ANIMATION";
+ case SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR:
+ return "HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR";
default:
return "Unknown=" + reason;
}
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index 4b968b45f122..f46626be48a8 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -47,7 +47,8 @@ import java.lang.annotation.Retention;
SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME,
SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED,
SoftInputShowHideReason.HIDE_RECENTS_ANIMATION,
- SoftInputShowHideReason.HIDE_BUBBLES})
+ SoftInputShowHideReason.HIDE_BUBBLES,
+ SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR})
public @interface SoftInputShowHideReason {
/** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
int SHOW_SOFT_INPUT = 0;
@@ -147,4 +148,17 @@ public @interface SoftInputShowHideReason {
* switching, or collapsing Bubbles.
*/
int HIDE_BUBBLES = 19;
+
+ /**
+ * Hide soft input when focusing the same window (e.g. screen turned-off and turn-on) which no
+ * valid focused editor.
+ *
+ * Note: From Android R, the window focus change callback is processed by InputDispatcher,
+ * some focus behavior changes (e.g. There are an activity with a dialog window, after
+ * screen turned-off and turned-on, before Android R the window focus sequence would be
+ * the activity first and then the dialog focused, however, in R the focus sequence would be
+ * only the dialog focused as it's the latest window with input focus) makes we need to hide
+ * soft-input when the same window focused again to align with the same behavior prior to R.
+ */
+ int HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR = 20;
}
diff --git a/core/java/com/android/internal/inputmethod/StartInputFlags.java b/core/java/com/android/internal/inputmethod/StartInputFlags.java
index 5a8d2c227256..ac83987ef12c 100644
--- a/core/java/com/android/internal/inputmethod/StartInputFlags.java
+++ b/core/java/com/android/internal/inputmethod/StartInputFlags.java
@@ -47,4 +47,10 @@ public @interface StartInputFlags {
* documented hence we probably need to revisit this though.
*/
int INITIAL_CONNECTION = 4;
+
+ /**
+ * The start input happens when the window gained focus to call
+ * {@code android.view.inputmethod.InputMethodManager#startInputAsyncOnWindowFocusGain}.
+ */
+ int WINDOW_GAINED_FOCUS = 8;
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 19dc2ed6daea..e60f7fc70757 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -51,6 +51,11 @@ public class InteractionJankMonitor {
public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 0;
public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 0;
public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 0;
+ public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS = 0;
+ public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON = 0;
+ public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 0;
+ public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 0;
+ public static final int CUJ_LAUNCHER_QUICK_SWITCH = 0;
private static final int NO_STATSD_LOGGING = -1;
@@ -78,7 +83,12 @@ public class InteractionJankMonitor {
CUJ_NOTIFICATION_SHADE_ROW_EXPAND,
CUJ_NOTIFICATION_SHADE_ROW_SWIPE,
CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
- CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE
+ CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE,
+ CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS,
+ CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON,
+ CUJ_LAUNCHER_APP_CLOSE_TO_HOME,
+ CUJ_LAUNCHER_APP_CLOSE_TO_PIP,
+ CUJ_LAUNCHER_QUICK_SWITCH,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4a0e26a5c7cb..308af99d465a 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -145,7 +145,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final boolean DEBUG = false;
public static final boolean DEBUG_ENERGY = false;
private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY;
- private static final boolean DEBUG_BINDER_STATS = true;
+ private static final boolean DEBUG_BINDER_STATS = false;
private static final boolean DEBUG_MEMORY = false;
private static final boolean DEBUG_HISTORY = false;
private static final boolean USE_OLD_HISTORY = false; // for debugging.
@@ -156,7 +156,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 188 + (USE_OLD_HISTORY ? 1000 : 0);
+ static final int VERSION = 189 + (USE_OLD_HISTORY ? 1000 : 0);
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -221,7 +221,8 @@ public class BatteryStatsImpl extends BatteryStats {
@VisibleForTesting
protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
@VisibleForTesting
- protected SystemServerCpuThreadReader mSystemServerCpuThreadReader;
+ protected SystemServerCpuThreadReader mSystemServerCpuThreadReader =
+ SystemServerCpuThreadReader.create();
private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
= new KernelMemoryBandwidthStats();
@@ -1014,14 +1015,21 @@ public class BatteryStatsImpl extends BatteryStats {
private long[] mCpuFreqs;
/**
+ * Times spent by the system server process grouped by cluster and CPU speed.
+ */
+ private LongSamplingCounterArray mSystemServerCpuTimesUs;
+
+ private long[] mTmpSystemServerCpuTimesUs;
+
+ /**
* Times spent by the system server threads grouped by cluster and CPU speed.
*/
- private LongSamplingCounter[][] mSystemServerThreadCpuTimesUs;
+ private LongSamplingCounterArray mSystemServerThreadCpuTimesUs;
/**
* Times spent by the system server threads handling incoming binder requests.
*/
- private LongSamplingCounter[][] mBinderThreadCpuTimesUs;
+ private LongSamplingCounterArray mBinderThreadCpuTimesUs;
@VisibleForTesting
protected PowerProfile mPowerProfile;
@@ -10610,8 +10618,6 @@ public class BatteryStatsImpl extends BatteryStats {
firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
}
- mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create();
-
if (mEstimatedBatteryCapacity == -1) {
// Initialize the estimated battery capacity to a known preset one.
mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity();
@@ -11291,6 +11297,7 @@ public class BatteryStatsImpl extends BatteryStats {
mTmpRailStats.reset();
+ resetIfNotNull(mSystemServerCpuTimesUs, false, elapsedRealtimeUs);
resetIfNotNull(mSystemServerThreadCpuTimesUs, false, elapsedRealtimeUs);
resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
@@ -12421,38 +12428,58 @@ public class BatteryStatsImpl extends BatteryStats {
SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
mSystemServerCpuThreadReader.readDelta();
+ if (systemServiceCpuThreadTimes == null) {
+ return;
+ }
- int index = 0;
int numCpuClusters = mPowerProfile.getNumCpuClusters();
- if (mSystemServerThreadCpuTimesUs == null) {
- mSystemServerThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][];
- mBinderThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][];
- }
- for (int cluster = 0; cluster < numCpuClusters; cluster++) {
- int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
- if (mSystemServerThreadCpuTimesUs[cluster] == null) {
- mSystemServerThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds];
- mBinderThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds];
- for (int speed = 0; speed < numSpeeds; speed++) {
- mSystemServerThreadCpuTimesUs[cluster][speed] =
- new LongSamplingCounter(mOnBatteryTimeBase);
- mBinderThreadCpuTimesUs[cluster][speed] =
- new LongSamplingCounter(mOnBatteryTimeBase);
- }
- }
- for (int speed = 0; speed < numSpeeds; speed++) {
- mSystemServerThreadCpuTimesUs[cluster][speed].addCountLocked(
- systemServiceCpuThreadTimes.threadCpuTimesUs[index]);
- mBinderThreadCpuTimesUs[cluster][speed].addCountLocked(
- systemServiceCpuThreadTimes.binderThreadCpuTimesUs[index]);
- index++;
- }
+ if (mSystemServerCpuTimesUs == null) {
+ mSystemServerCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
+ mSystemServerThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
+ mBinderThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
+ }
+ mSystemServerThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.threadCpuTimesUs);
+ mBinderThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
+
+ long totalCpuTimeAllThreads = 0;
+ for (int index = systemServiceCpuThreadTimes.threadCpuTimesUs.length - 1; index >= 0;
+ index--) {
+ totalCpuTimeAllThreads += systemServiceCpuThreadTimes.threadCpuTimesUs[index];
+ }
+
+ // Estimate per cluster per frequency CPU time for the entire process
+ // by distributing the total process CPU time proportionately to how much
+ // CPU time its threads took on those clusters/frequencies. This algorithm
+ // works more accurately when when we have equally distributed concurrency.
+ // TODO(b/169279846): obtain actual process CPU times from the kernel
+ long processCpuTime = systemServiceCpuThreadTimes.processCpuTimeUs;
+ if (mTmpSystemServerCpuTimesUs == null) {
+ mTmpSystemServerCpuTimesUs =
+ new long[systemServiceCpuThreadTimes.threadCpuTimesUs.length];
}
+ for (int index = systemServiceCpuThreadTimes.threadCpuTimesUs.length - 1; index >= 0;
+ index--) {
+ mTmpSystemServerCpuTimesUs[index] =
+ processCpuTime * systemServiceCpuThreadTimes.threadCpuTimesUs[index]
+ / totalCpuTimeAllThreads;
+
+ }
+
+ mSystemServerCpuTimesUs.addCountLocked(mTmpSystemServerCpuTimesUs);
+
if (DEBUG_BINDER_STATS) {
Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)");
- long binderThreadTimeMs = 0;
+ long totalCpuTimeMs = 0;
long totalThreadTimeMs = 0;
+ long binderThreadTimeMs = 0;
int cpuIndex = 0;
+ final long[] systemServerCpuTimesUs =
+ mSystemServerCpuTimesUs.getCountsLocked(0);
+ final long[] systemServerThreadCpuTimesUs =
+ mSystemServerThreadCpuTimesUs.getCountsLocked(0);
+ final long[] binderThreadCpuTimesUs =
+ mBinderThreadCpuTimesUs.getCountsLocked(0);
+ int index = 0;
for (int cluster = 0; cluster < numCpuClusters; cluster++) {
StringBuilder sb = new StringBuilder();
sb.append("cpu").append(cpuIndex).append(": [");
@@ -12461,15 +12488,14 @@ public class BatteryStatsImpl extends BatteryStats {
if (speed != 0) {
sb.append(", ");
}
- long totalCountMs =
- mSystemServerThreadCpuTimesUs[cluster][speed].getCountLocked(0) / 1000;
- long binderCountMs = mBinderThreadCpuTimesUs[cluster][speed].getCountLocked(0)
- / 1000;
+ long totalCountMs = systemServerThreadCpuTimesUs[index] / 1000;
+ long binderCountMs = binderThreadCpuTimesUs[index] / 1000;
sb.append(String.format("%d/%d(%.1f%%)",
binderCountMs,
totalCountMs,
totalCountMs != 0 ? (double) binderCountMs * 100 / totalCountMs : 0));
+ totalCpuTimeMs += systemServerCpuTimesUs[index] / 1000;
totalThreadTimeMs += totalCountMs;
binderThreadTimeMs += binderCountMs;
index++;
@@ -12477,6 +12503,8 @@ public class BatteryStatsImpl extends BatteryStats {
cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster);
Slog.d(TAG, sb.toString());
}
+
+ Slog.d(TAG, "Total system server CPU time (ms): " + totalCpuTimeMs);
Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTimeMs);
Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)",
binderThreadTimeMs,
@@ -13715,7 +13743,7 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
- public long getSystemServiceTimeAtCpuSpeed(int cluster, int step) {
+ public long[] getSystemServiceTimeAtCpuSpeeds() {
// Estimates the time spent by the system server handling incoming binder requests.
//
// The data that we can get from the kernel is this:
@@ -13731,7 +13759,7 @@ public class BatteryStatsImpl extends BatteryStats {
// - These 10 threads spent 1000 ms of CPU time in aggregate
// - Of the 10 threads 4 were execute exclusively incoming binder calls.
// - These 4 "binder" threads consumed 600 ms of CPU time in aggregate
- // - The real time spent by the system server UID doing all of this is, say, 200 ms.
+ // - The real time spent by the system server process doing all of this is, say, 200 ms.
//
// We will assume that power consumption is proportional to the time spent by the CPU
// across all threads. This is a crude assumption, but we don't have more detailed data.
@@ -13745,41 +13773,29 @@ public class BatteryStatsImpl extends BatteryStats {
// of the total power consumed by incoming binder calls for the given cluster/speed
// combination.
- if (mSystemServerThreadCpuTimesUs == null) {
- return 0;
- }
-
- if (cluster < 0 || cluster >= mSystemServerThreadCpuTimesUs.length) {
- return 0;
- }
-
- final LongSamplingCounter[] threadTimesForCluster = mSystemServerThreadCpuTimesUs[cluster];
-
- if (step < 0 || step >= threadTimesForCluster.length) {
- return 0;
- }
-
- Uid systemUid = mUidStats.get(Process.SYSTEM_UID);
- if (systemUid == null) {
- return 0;
+ if (mSystemServerCpuTimesUs == null) {
+ return null;
}
- final long uidTimeAtCpuSpeedUs = systemUid.getTimeAtCpuSpeed(cluster, step,
+ final long[] systemServerCpuTimesUs = mSystemServerCpuTimesUs.getCountsLocked(
+ BatteryStats.STATS_SINCE_CHARGED);
+ final long [] systemServerThreadCpuTimesUs = mSystemServerThreadCpuTimesUs.getCountsLocked(
+ BatteryStats.STATS_SINCE_CHARGED);
+ final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked(
BatteryStats.STATS_SINCE_CHARGED);
- if (uidTimeAtCpuSpeedUs == 0) {
- return 0;
- }
- final long uidThreadTimeUs =
- threadTimesForCluster[step].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+ final int size = systemServerCpuTimesUs.length;
+ final long[] results = new long[size];
- if (uidThreadTimeUs == 0) {
- return 0;
- }
+ for (int i = 0; i < size; i++) {
+ if (systemServerThreadCpuTimesUs[i] == 0) {
+ continue;
+ }
- final long binderThreadTimeUs = mBinderThreadCpuTimesUs[cluster][step].getCountLocked(
- BatteryStats.STATS_SINCE_CHARGED);
- return uidTimeAtCpuSpeedUs * binderThreadTimeUs / uidThreadTimeUs;
+ results[i] = systemServerCpuTimesUs[i] * binderThreadCpuTimesUs[i]
+ / systemServerThreadCpuTimesUs[i];
+ }
+ return results;
}
/**
@@ -14181,18 +14197,14 @@ public class BatteryStatsImpl extends BatteryStats {
updateSystemServiceCallStats();
if (mSystemServerThreadCpuTimesUs != null) {
pw.println("Per UID System server binder time in ms:");
+ long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds();
for (int i = 0; i < size; i++) {
int u = mUidStats.keyAt(i);
Uid uid = mUidStats.get(u);
double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage();
-
long timeUs = 0;
- for (int cluster = 0; cluster < mSystemServerThreadCpuTimesUs.length; cluster++) {
- int numSpeeds = mSystemServerThreadCpuTimesUs[cluster].length;
- for (int speed = 0; speed < numSpeeds; speed++) {
- timeUs += getSystemServiceTimeAtCpuSpeed(cluster, speed)
- * proportionalSystemServiceUsage;
- }
+ for (int j = systemServiceTimeAtCpuSpeeds.length - 1; j >= 0; j--) {
+ timeUs += systemServiceTimeAtCpuSpeeds[j] * proportionalSystemServiceUsage;
}
pw.print(" ");
@@ -15728,8 +15740,10 @@ public class BatteryStatsImpl extends BatteryStats {
mUidStats.append(uid, u);
}
- mSystemServerThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in);
- mBinderThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in);
+ mSystemServerCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase);
+ mSystemServerThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in,
+ mOnBatteryTimeBase);
+ mBinderThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase);
}
public void writeToParcel(Parcel out, int flags) {
@@ -15778,7 +15792,7 @@ public class BatteryStatsImpl extends BatteryStats {
mScreenOnTimer.writeToParcel(out, uSecRealtime);
mScreenDozeTimer.writeToParcel(out, uSecRealtime);
- for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
+ for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].writeToParcel(out, uSecRealtime);
}
mInteractiveTimer.writeToParcel(out, uSecRealtime);
@@ -15794,7 +15808,7 @@ public class BatteryStatsImpl extends BatteryStats {
mPhoneSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
}
mPhoneSignalScanningTimer.writeToParcel(out, uSecRealtime);
- for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
+ for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i].writeToParcel(out, uSecRealtime);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
@@ -15809,18 +15823,18 @@ public class BatteryStatsImpl extends BatteryStats {
mWifiMulticastWakelockTimer.writeToParcel(out, uSecRealtime);
mWifiOnTimer.writeToParcel(out, uSecRealtime);
mGlobalWifiRunningTimer.writeToParcel(out, uSecRealtime);
- for (int i=0; i<NUM_WIFI_STATES; i++) {
+ for (int i = 0; i < NUM_WIFI_STATES; i++) {
mWifiStateTimer[i].writeToParcel(out, uSecRealtime);
}
- for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+ for (int i = 0; i < NUM_WIFI_SUPPL_STATES; i++) {
mWifiSupplStateTimer[i].writeToParcel(out, uSecRealtime);
}
- for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+ for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
}
mWifiActiveTimer.writeToParcel(out, uSecRealtime);
mWifiActivity.writeToParcel(out, 0);
- for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ for (int i = 0; i < GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
mGpsSignalQualityTimer[i].writeToParcel(out, uSecRealtime);
}
mBluetoothActivity.writeToParcel(out, 0);
@@ -15930,8 +15944,9 @@ public class BatteryStatsImpl extends BatteryStats {
} else {
out.writeInt(0);
}
- writeCpuSpeedCountersToParcel(out, mSystemServerThreadCpuTimesUs);
- writeCpuSpeedCountersToParcel(out, mBinderThreadCpuTimesUs);
+ LongSamplingCounterArray.writeToParcel(out, mSystemServerCpuTimesUs);
+ LongSamplingCounterArray.writeToParcel(out, mSystemServerThreadCpuTimesUs);
+ LongSamplingCounterArray.writeToParcel(out, mBinderThreadCpuTimesUs);
}
private void writeCpuSpeedCountersToParcel(Parcel out, LongSamplingCounter[][] counters) {
diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
new file mode 100644
index 000000000000..0578b8976037
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static android.os.Process.PROC_OUT_LONG;
+import static android.os.Process.PROC_SPACE_TERM;
+
+import android.annotation.Nullable;
+import android.os.Process;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Iterates over all threads owned by a given process, and return the CPU usage for
+ * each thread. The CPU usage statistics contain the amount of time spent in a frequency band. CPU
+ * usage is collected using {@link ProcTimeInStateReader}.
+ */
+public class KernelSingleProcessCpuThreadReader {
+
+ private static final String TAG = "KernelSingleProcCpuThreadRdr";
+
+ private static final boolean DEBUG = false;
+
+ /**
+ * The name of the file to read CPU statistics from, must be found in {@code
+ * /proc/$PID/task/$TID}
+ */
+ private static final String CPU_STATISTICS_FILENAME = "time_in_state";
+
+ private static final String PROC_STAT_FILENAME = "stat";
+
+ /** Directory under /proc/$PID containing CPU stats files for threads */
+ public static final String THREAD_CPU_STATS_DIRECTORY = "task";
+
+ /** Default mount location of the {@code proc} filesystem */
+ private static final Path DEFAULT_PROC_PATH = Paths.get("/proc");
+
+ /** The initial {@code time_in_state} file for {@link ProcTimeInStateReader} */
+ private static final Path INITIAL_TIME_IN_STATE_PATH = Paths.get("self/time_in_state");
+
+ /** See https://man7.org/linux/man-pages/man5/proc.5.html */
+ private static final int[] PROCESS_FULL_STATS_FORMAT = new int[]{
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM | PROC_OUT_LONG, // 14: utime
+ PROC_SPACE_TERM | PROC_OUT_LONG, // 15: stime
+ // Ignore remaining fields
+ };
+
+ private final long[] mProcessFullStatsData = new long[2];
+
+ private static final int PROCESS_FULL_STAT_UTIME = 0;
+ private static final int PROCESS_FULL_STAT_STIME = 1;
+
+ /** Used to read and parse {@code time_in_state} files */
+ private final ProcTimeInStateReader mProcTimeInStateReader;
+
+ private final int mPid;
+
+ /** Where the proc filesystem is mounted */
+ private final Path mProcPath;
+
+ // How long a CPU jiffy is in milliseconds.
+ private final long mJiffyMillis;
+
+ // Path: /proc/<pid>/stat
+ private final String mProcessStatFilePath;
+
+ // Path: /proc/<pid>/task
+ private final Path mThreadsDirectoryPath;
+
+ /**
+ * Count of frequencies read from the {@code time_in_state} file. Read from {@link
+ * #mProcTimeInStateReader#getCpuFrequenciesKhz()}.
+ */
+ private int mFrequencyCount;
+
+ /**
+ * Create with a path where `proc` is mounted. Used primarily for testing
+ *
+ * @param pid PID of the process whose threads are to be read.
+ * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc})
+ */
+ @VisibleForTesting
+ public KernelSingleProcessCpuThreadReader(
+ int pid,
+ Path procPath) throws IOException {
+ mPid = pid;
+ mProcPath = procPath;
+ mProcTimeInStateReader = new ProcTimeInStateReader(
+ mProcPath.resolve(INITIAL_TIME_IN_STATE_PATH));
+ long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK);
+ mJiffyMillis = 1000 / jiffyHz;
+ mProcessStatFilePath =
+ mProcPath.resolve(String.valueOf(mPid)).resolve(PROC_STAT_FILENAME).toString();
+ mThreadsDirectoryPath =
+ mProcPath.resolve(String.valueOf(mPid)).resolve(THREAD_CPU_STATS_DIRECTORY);
+ }
+
+ /**
+ * Create the reader and handle exceptions during creation
+ *
+ * @return the reader, null if an exception was thrown during creation
+ */
+ @Nullable
+ public static KernelSingleProcessCpuThreadReader create(int pid) {
+ try {
+ return new KernelSingleProcessCpuThreadReader(pid, DEFAULT_PROC_PATH);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to initialize KernelSingleProcessCpuThreadReader", e);
+ return null;
+ }
+ }
+
+ /**
+ * Get the CPU frequencies that correspond to the times reported in {@link
+ * ThreadCpuUsage#usageTimesMillis}
+ */
+ public int getCpuFrequencyCount() {
+ if (mFrequencyCount == 0) {
+ mFrequencyCount = mProcTimeInStateReader.getFrequenciesKhz().length;
+ }
+ return mFrequencyCount;
+ }
+
+ /**
+ * Get the total and per-thread CPU usage of the process with the PID specified in the
+ * constructor.
+ */
+ @Nullable
+ public ProcessCpuUsage getProcessCpuUsage() {
+ if (DEBUG) {
+ Slog.d(TAG, "Reading CPU thread usages with directory " + mProcPath + " process ID "
+ + mPid);
+ }
+
+ if (!Process.readProcFile(mProcessStatFilePath, PROCESS_FULL_STATS_FORMAT, null,
+ mProcessFullStatsData, null)) {
+ Slog.e(TAG, "Failed to read process stat file " + mProcessStatFilePath);
+ return null;
+ }
+
+ long utime = mProcessFullStatsData[PROCESS_FULL_STAT_UTIME];
+ long stime = mProcessFullStatsData[PROCESS_FULL_STAT_STIME];
+
+ long processCpuTimeMillis = (utime + stime) * mJiffyMillis;
+
+ final ArrayList<ThreadCpuUsage> threadCpuUsages = new ArrayList<>();
+ try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(mThreadsDirectoryPath)) {
+ for (Path threadDirectory : threadPaths) {
+ ThreadCpuUsage threadCpuUsage = getThreadCpuUsage(threadDirectory);
+ if (threadCpuUsage == null) {
+ continue;
+ }
+ threadCpuUsages.add(threadCpuUsage);
+ }
+ } catch (IOException | DirectoryIteratorException e) {
+ // Expected when a process finishes
+ return null;
+ }
+
+ // If we found no threads, then the process has exited while we were reading from it
+ if (threadCpuUsages.isEmpty()) {
+ return null;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Read CPU usage of " + threadCpuUsages.size() + " threads");
+ }
+ return new ProcessCpuUsage(processCpuTimeMillis, threadCpuUsages);
+ }
+
+ /**
+ * Get a thread's CPU usage
+ *
+ * @param threadDirectory the {@code /proc} directory of the thread
+ * @return thread CPU usage. Null if the thread exited and its {@code proc} directory was
+ * removed while collecting information
+ */
+ @Nullable
+ private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) {
+ // Get the thread ID from the directory name
+ final int threadId;
+ try {
+ final String directoryName = threadDirectory.getFileName().toString();
+ threadId = Integer.parseInt(directoryName);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Failed to parse thread ID when iterating over /proc/*/task", e);
+ return null;
+ }
+
+ // Get the CPU statistics from the directory
+ final Path threadCpuStatPath = threadDirectory.resolve(CPU_STATISTICS_FILENAME);
+ final long[] cpuUsages = mProcTimeInStateReader.getUsageTimesMillis(threadCpuStatPath);
+ if (cpuUsages == null) {
+ return null;
+ }
+
+ return new ThreadCpuUsage(threadId, cpuUsages);
+ }
+
+ /** CPU usage of a process and all of its threads */
+ public static class ProcessCpuUsage {
+ public final long cpuTimeMillis;
+ public final List<ThreadCpuUsage> threadCpuUsages;
+
+ ProcessCpuUsage(long cpuTimeMillis, List<ThreadCpuUsage> threadCpuUsages) {
+ this.cpuTimeMillis = cpuTimeMillis;
+ this.threadCpuUsages = threadCpuUsages;
+ }
+ }
+
+ /** CPU usage of a thread */
+ public static class ThreadCpuUsage {
+ public final int threadId;
+ public final long[] usageTimesMillis;
+
+ ThreadCpuUsage(int threadId, long[] usageTimesMillis) {
+ this.threadId = threadId;
+ this.usageTimesMillis = usageTimesMillis;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
index 3aa2390375ec..d9f0dc0ae795 100644
--- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
+++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
@@ -16,25 +16,28 @@
package com.android.internal.os;
+import android.annotation.Nullable;
import android.os.Process;
import com.android.internal.annotations.VisibleForTesting;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
/**
* Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage
* by various threads of the System Server.
*/
public class SystemServerCpuThreadReader {
- private KernelCpuThreadReader mKernelCpuThreadReader;
+ private final KernelSingleProcessCpuThreadReader mKernelCpuThreadReader;
private int[] mBinderThreadNativeTids = new int[0]; // Sorted
- private int[] mThreadCpuTimesUs;
- private int[] mBinderThreadCpuTimesUs;
+ private long mProcessCpuTimeUs;
+ private long[] mThreadCpuTimesUs;
+ private long[] mBinderThreadCpuTimesUs;
+ private long mLastProcessCpuTimeUs;
private long[] mLastThreadCpuTimesUs;
private long[] mLastBinderThreadCpuTimesUs;
@@ -42,6 +45,8 @@ public class SystemServerCpuThreadReader {
* Times (in microseconds) spent by the system server UID.
*/
public static class SystemServiceCpuThreadTimes {
+ // The entire process
+ public long processCpuTimeUs;
// All threads
public long[] threadCpuTimesUs;
// Just the threads handling incoming binder calls
@@ -55,22 +60,16 @@ public class SystemServerCpuThreadReader {
*/
public static SystemServerCpuThreadReader create() {
return new SystemServerCpuThreadReader(
- KernelCpuThreadReader.create(0, uid -> uid == Process.myUid()));
+ KernelSingleProcessCpuThreadReader.create(Process.myPid()));
}
@VisibleForTesting
- public SystemServerCpuThreadReader(Path procPath, int systemServerUid) throws IOException {
- this(new KernelCpuThreadReader(0, uid -> uid == systemServerUid, null, null,
- new KernelCpuThreadReader.Injector() {
- @Override
- public int getUidForPid(int pid) {
- return systemServerUid;
- }
- }));
+ public SystemServerCpuThreadReader(Path procPath, int pid) throws IOException {
+ this(new KernelSingleProcessCpuThreadReader(pid, procPath));
}
@VisibleForTesting
- public SystemServerCpuThreadReader(KernelCpuThreadReader kernelCpuThreadReader) {
+ public SystemServerCpuThreadReader(KernelSingleProcessCpuThreadReader kernelCpuThreadReader) {
mKernelCpuThreadReader = kernelCpuThreadReader;
}
@@ -82,11 +81,12 @@ public class SystemServerCpuThreadReader {
/**
* Returns delta of CPU times, per thread, since the previous call to this method.
*/
+ @Nullable
public SystemServiceCpuThreadTimes readDelta() {
+ int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount();
if (mBinderThreadCpuTimesUs == null) {
- int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz().length;
- mThreadCpuTimesUs = new int[numCpuFrequencies];
- mBinderThreadCpuTimesUs = new int[numCpuFrequencies];
+ mThreadCpuTimesUs = new long[numCpuFrequencies];
+ mBinderThreadCpuTimesUs = new long[numCpuFrequencies];
mLastThreadCpuTimesUs = new long[numCpuFrequencies];
mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies];
@@ -95,49 +95,47 @@ public class SystemServerCpuThreadReader {
mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies];
}
+ mProcessCpuTimeUs = 0;
Arrays.fill(mThreadCpuTimesUs, 0);
Arrays.fill(mBinderThreadCpuTimesUs, 0);
- ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsage =
+ KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
mKernelCpuThreadReader.getProcessCpuUsage();
- int processCpuUsageSize = processCpuUsage.size();
- for (int i = 0; i < processCpuUsageSize; i++) {
- KernelCpuThreadReader.ProcessCpuUsage pcu = processCpuUsage.get(i);
- ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = pcu.threadCpuUsages;
- if (threadCpuUsages != null) {
- int threadCpuUsagesSize = threadCpuUsages.size();
- for (int j = 0; j < threadCpuUsagesSize; j++) {
- KernelCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(j);
- boolean isBinderThread =
- Arrays.binarySearch(mBinderThreadNativeTids, tcu.threadId) >= 0;
-
- final int len = Math.min(tcu.usageTimesMillis.length, mThreadCpuTimesUs.length);
- for (int k = 0; k < len; k++) {
- int usageTimeUs = tcu.usageTimesMillis[k] * 1000;
- mThreadCpuTimesUs[k] += usageTimeUs;
- if (isBinderThread) {
- mBinderThreadCpuTimesUs[k] += usageTimeUs;
- }
- }
+ if (processCpuUsage == null) {
+ return null;
+ }
+
+ mProcessCpuTimeUs = processCpuUsage.cpuTimeMillis * 1000;
+
+ List<KernelSingleProcessCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
+ processCpuUsage.threadCpuUsages;
+ int threadCpuUsagesSize = threadCpuUsages.size();
+ for (int i = 0; i < threadCpuUsagesSize; i++) {
+ KernelSingleProcessCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(i);
+ boolean isBinderThread =
+ Arrays.binarySearch(mBinderThreadNativeTids, tcu.threadId) >= 0;
+ for (int k = 0; k < numCpuFrequencies; k++) {
+ long usageTimeUs = tcu.usageTimesMillis[k] * 1000;
+ mThreadCpuTimesUs[k] += usageTimeUs;
+ if (isBinderThread) {
+ mBinderThreadCpuTimesUs[k] += usageTimeUs;
}
}
}
for (int i = 0; i < mThreadCpuTimesUs.length; i++) {
- if (mThreadCpuTimesUs[i] < mLastThreadCpuTimesUs[i]) {
- mDeltaCpuThreadTimes.threadCpuTimesUs[i] = mThreadCpuTimesUs[i];
- mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i];
- } else {
- mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
- mThreadCpuTimesUs[i] - mLastThreadCpuTimesUs[i];
- mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
- mBinderThreadCpuTimesUs[i] - mLastBinderThreadCpuTimesUs[i];
- }
+ mDeltaCpuThreadTimes.processCpuTimeUs =
+ Math.max(0, mProcessCpuTimeUs - mLastProcessCpuTimeUs);
+ mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
+ Math.max(0, mThreadCpuTimesUs[i] - mLastThreadCpuTimesUs[i]);
+ mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
+ Math.max(0, mBinderThreadCpuTimesUs[i] - mLastBinderThreadCpuTimesUs[i]);
mLastThreadCpuTimesUs[i] = mThreadCpuTimesUs[i];
mLastBinderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i];
}
+ mLastProcessCpuTimeUs = mProcessCpuTimeUs;
+
return mDeltaCpuThreadTimes;
}
-
}
diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
index 481b901b3c69..fc36e50950e9 100644
--- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -34,7 +34,9 @@ public class SystemServicePowerCalculator extends PowerCalculator {
private final PowerProfile mPowerProfile;
private final BatteryStats mBatteryStats;
// Tracks system server CPU [cluster][speed] power in milliAmp-microseconds
- private double[][] mSystemServicePowerMaUs;
+ // Data organized like this:
+ // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...}
+ private double[] mSystemServicePowerMaUs;
public SystemServicePowerCalculator(PowerProfile powerProfile, BatteryStats batteryStats) {
mPowerProfile = powerProfile;
@@ -50,37 +52,41 @@ public class SystemServicePowerCalculator extends PowerCalculator {
updateSystemServicePower();
}
- double cpuPowerMaUs = 0;
- int numCpuClusters = mPowerProfile.getNumCpuClusters();
- for (int cluster = 0; cluster < numCpuClusters; cluster++) {
- final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
- for (int speed = 0; speed < numSpeeds; speed++) {
- cpuPowerMaUs += mSystemServicePowerMaUs[cluster][speed] * proportionalUsage;
+ if (mSystemServicePowerMaUs != null) {
+ double cpuPowerMaUs = 0;
+ for (int i = 0; i < mSystemServicePowerMaUs.length; i++) {
+ cpuPowerMaUs += mSystemServicePowerMaUs[i] * proportionalUsage;
}
- }
- app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
+ app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
+ }
}
}
private void updateSystemServicePower() {
+ final long[] systemServiceTimeAtCpuSpeeds = mBatteryStats.getSystemServiceTimeAtCpuSpeeds();
+ if (systemServiceTimeAtCpuSpeeds == null) {
+ return;
+ }
+
+ if (mSystemServicePowerMaUs == null) {
+ mSystemServicePowerMaUs = new double[systemServiceTimeAtCpuSpeeds.length];
+ }
+ int index = 0;
final int numCpuClusters = mPowerProfile.getNumCpuClusters();
- mSystemServicePowerMaUs = new double[numCpuClusters][];
for (int cluster = 0; cluster < numCpuClusters; cluster++) {
final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
- mSystemServicePowerMaUs[cluster] = new double[numSpeeds];
for (int speed = 0; speed < numSpeeds; speed++) {
- mSystemServicePowerMaUs[cluster][speed] =
- mBatteryStats.getSystemServiceTimeAtCpuSpeed(cluster, speed)
+ mSystemServicePowerMaUs[index] =
+ systemServiceTimeAtCpuSpeeds[index]
* mPowerProfile.getAveragePowerForCpuCore(cluster, speed);
+ index++;
}
}
+
if (DEBUG) {
- Log.d(TAG, "System service power per CPU cluster and frequency");
- for (int cluster = 0; cluster < numCpuClusters; cluster++) {
- Log.d(TAG, "Cluster[" + cluster + "]: "
- + Arrays.toString(mSystemServicePowerMaUs[cluster]));
- }
+ Log.d(TAG, "System service power per CPU cluster and frequency:"
+ + Arrays.toString(mSystemServicePowerMaUs));
}
}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index d5f54a199828..fff9ac9e49b7 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -43,7 +43,6 @@ public class BaseIWindow extends IWindow.Stub {
public BaseIWindow() {}
private IWindowSession mSession;
- public int mSeq;
public void setSession(IWindowSession session) {
mSession = session;
@@ -140,12 +139,6 @@ public class BaseIWindow extends IWindow.Stub {
}
@Override
- public void dispatchSystemUiVisibilityChanged(int seq, int globalUi,
- int localValue, int localChanges) {
- mSeq = seq;
- }
-
- @Override
public void dispatchWallpaperCommand(String action, int x, int y,
int z, Bundle extras, boolean sync) {
if (sync) {
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 45090320c192..c9443b002133 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -33,4 +33,5 @@ oneway interface IInputMethodClient {
void reportPreRendered(in EditorInfo info);
void applyImeVisibility(boolean setVisible);
void updateActivityViewToScreenMatrix(int bindSequence, in float[] matrixValues);
+ void setImeTraceEnabled(boolean enabled);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index a1cbd3fcae79..5a06273bb173 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -77,4 +77,6 @@ interface IInputMethodManager {
void removeImeSurface();
/** Remove the IME surface. Requires passing the currently focused window. */
void removeImeSurfaceFromWindow(in IBinder windowToken);
+ void startProtoDump(in byte[] clientProtoDump);
+ boolean isImeTraceEnabled();
}
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index ddee81a649af..ff3543c837eb 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -17,9 +17,6 @@
package com.android.internal.widget;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.method.KeyListener;
@@ -30,8 +27,6 @@ import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputContentInfo;
-import android.widget.RichContentReceiver;
import android.widget.TextView;
public class EditableInputConnection extends BaseInputConnection {
@@ -186,28 +181,6 @@ public class EditableInputConnection extends BaseInputConnection {
}
@Override
- public boolean commitContent(InputContentInfo content, int flags, Bundle opts) {
- int targetSdkVersion = mTextView.getContext().getApplicationInfo().targetSdkVersion;
- if (targetSdkVersion <= Build.VERSION_CODES.R) {
- return false;
- }
-
- final ClipDescription description = content.getDescription();
- final RichContentReceiver<TextView> receiver = mTextView.getRichContentReceiver();
- if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
- try {
- content.requestPermission();
- } catch (Exception e) {
- // TODO(b/147299828): Can we catch SecurityException instead?
- Log.w(TAG, "Can't insert content from IME; requestPermission() failed: " + e);
- return false; // Can't insert the content if we don't have permission to read it
- }
- }
- ClipData clip = new ClipData(description, new ClipData.Item(content.getContentUri()));
- return receiver.onReceive(mTextView, clip, RichContentReceiver.SOURCE_INPUT_METHOD, 0);
- }
-
- @Override
public boolean requestCursorUpdates(int cursorUpdateMode) {
if (DEBUG) Log.v(TAG, "requestUpdateCursorAnchorInfo " + cursorUpdateMode);
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index be68c4aee278..243540693785 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -84,23 +84,31 @@ static int getFdCount() {
}
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) {
+ status_t status;
String8 name;
+ CursorWindow* window;
+
const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
name.setTo(nameStr);
env->ReleaseStringUTFChars(nameObj, nameStr);
- CursorWindow* window;
- status_t status = CursorWindow::create(name, cursorWindowSize, &window);
+ if (cursorWindowSize < 0) {
+ status = INVALID_OPERATION;
+ goto fail;
+ }
+ status = CursorWindow::create(name, cursorWindowSize, &window);
if (status || !window) {
- jniThrowExceptionFmt(env,
- "android/database/CursorWindowAllocationException",
- "Could not allocate CursorWindow '%s' of size %d due to error %d.",
- name.string(), cursorWindowSize, status);
- return 0;
+ goto fail;
}
LOG_WINDOW("nativeInitializeEmpty: window = %p", window);
return reinterpret_cast<jlong>(window);
+
+fail:
+ jniThrowExceptionFmt(env, "android/database/CursorWindowAllocationException",
+ "Could not allocate CursorWindow '%s' of size %d due to error %d.",
+ name.string(), cursorWindowSize, status);
+ return 0;
}
static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h
index d629e0dae6dd..013c65faa241 100644
--- a/core/jni/core_jni_helpers.h
+++ b/core/jni/core_jni_helpers.h
@@ -90,6 +90,12 @@ static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className,
return res;
}
+static inline jobject jniGetReferent(JNIEnv* env, jobject ref) {
+ jclass cls = FindClassOrDie(env, "java/lang/ref/Reference");
+ jmethodID get = GetMethodIDOrDie(env, cls, "get", "()Ljava/lang/Object;");
+ return env->CallObjectMethod(ref, get);
+}
+
/**
* Read the specified field from jobject, and convert to std::string.
* If the field cannot be obtained, return defaultValue.
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 4892faaceafe..542d26fa233e 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -14,8 +14,12 @@ per-file settings_enums.proto=tmfang@google.com
# Frameworks
ogunwale@google.com
jjaggi@google.com
+roosa@google.com
per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
+# Biometrics
+kchyn@google.com
+
# Launcher
hyunyoungs@google.com
diff --git a/core/proto/android/server/peopleservice.proto b/core/proto/android/server/peopleservice.proto
index 59556c4414ce..c465233036c4 100644
--- a/core/proto/android/server/peopleservice.proto
+++ b/core/proto/android/server/peopleservice.proto
@@ -46,6 +46,10 @@ message ConversationInfoProto {
// The notification channel id of the conversation.
optional string notification_channel_id = 4 [(.android.privacy).dest = DEST_EXPLICIT];
+ // The parent notification channel ID of the conversation. This is the notification channel where
+ // the notifications are posted before this conversation is customized by the user.
+ optional string parent_notification_channel_id = 8 [(.android.privacy).dest = DEST_EXPLICIT];
+
// Integer representation of shortcut bit flags.
optional int32 shortcut_flags = 5;
@@ -54,6 +58,11 @@ message ConversationInfoProto {
// The phone number of the contact.
optional string contact_phone_number = 7 [(.android.privacy).dest = DEST_EXPLICIT];
+
+ // The timestamp of the last event in millis.
+ optional int64 last_event_timestamp = 9;
+
+ // Next tag: 10
}
// On disk data of events.
diff --git a/core/proto/android/stats/style/style_enums.proto b/core/proto/android/stats/style/style_enums.proto
index f3f491ff34cd..828e4127a708 100644
--- a/core/proto/android/stats/style/style_enums.proto
+++ b/core/proto/android/stats/style/style_enums.proto
@@ -38,6 +38,9 @@ enum Action {
LIVE_WALLPAPER_APPLIED = 16;
LIVE_WALLPAPER_INFO_SELECT = 17;
LIVE_WALLPAPER_CUSTOMIZE_SELECT = 18;
+ LIVE_WALLPAPER_QUESTIONNAIRE_SELECT = 19;
+ LIVE_WALLPAPER_QUESTIONNAIRE_APPLIED = 20;
+ LIVE_WALLPAPER_EFFECT_SHOW = 21;
}
enum LocationPreference {
@@ -46,3 +49,9 @@ enum LocationPreference {
LOCATION_CURRENT = 2;
LOCATION_MANUAL = 3;
}
+
+enum DatePreference {
+ DATE_PREFERENCE_UNSPECIFIED = 0;
+ DATE_UNAVAILABLE = 1;
+ DATE_MANUAL = 2;
+}
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index f14e3ed1872d..b56bd2bae29a 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -159,3 +159,50 @@ enum SimStateEnum {
*/
SIM_STATE_PRESENT = 11;
}
+
+// Format of SMS message
+enum SmsFormatEnum {
+ /** Unknown format */
+ SMS_FORMAT_UNKNOWN = 0;
+ /** Format compliant with 3GPP TS 23.040 */
+ SMS_FORMAT_3GPP = 1;
+ /** Format compliant with 3GPP2 TS C.S0015-B */
+ SMS_FORMAT_3GPP2 = 2;
+}
+
+// Technology used to carry an SMS message
+enum SmsTechEnum {
+ /**
+ * Unknown SMS technology used to carry the SMS.
+ * This value is also used for injected SMS.
+ */
+ SMS_TECH_UNKNOWN = 0;
+ /** The SMS was carried over CS bearer in 3GPP network */
+ SMS_TECH_CS_3GPP = 1;
+ /** The SMS was carried over CS bearer in 3GPP2 network */
+ SMS_TECH_CS_3GPP2 = 2;
+ /** The SMS was carried over IMS */
+ SMS_TECH_IMS = 3;
+}
+
+// Types of SMS message
+enum SmsTypeEnum {
+ /** Normal type. */
+ SMS_TYPE_NORMAL = 0;
+ /** SMS-PP (point-to-point). */
+ SMS_TYPE_SMS_PP = 1;
+ /** Voicemail indication. */
+ SMS_TYPE_VOICEMAIL_INDICATION = 2;
+ /** Type 0 message (3GPP TS 23.040 9.2.3.9). */
+ SMS_TYPE_ZERO = 3;
+ /** WAP-PUSH message. */
+ SMS_TYPE_WAP_PUSH = 4;
+}
+
+// SMS errors
+enum SmsIncomingErrorEnum {
+ SMS_SUCCESS = 0;
+ SMS_ERROR_GENERIC = 1;
+ SMS_ERROR_NO_MEMORY = 2;
+ SMS_ERROR_NOT_SUPPORTED = 3;
+}
diff --git a/core/proto/android/view/imeinsetssourceconsumer.proto b/core/proto/android/view/imeinsetssourceconsumer.proto
index 680916345a31..5bee81bdc7cd 100644
--- a/core/proto/android/view/imeinsetssourceconsumer.proto
+++ b/core/proto/android/view/imeinsetssourceconsumer.proto
@@ -17,6 +17,7 @@
syntax = "proto2";
import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto";
+import "frameworks/base/core/proto/android/view/insetssourceconsumer.proto";
package android.view;
@@ -26,6 +27,7 @@ option java_multiple_files = true;
* Represents a {@link android.view.ImeInsetsSourceConsumer} object.
*/
message ImeInsetsSourceConsumerProto {
- optional .android.view.inputmethod.EditorInfoProto focused_editor = 1;
- optional bool is_requested_visible_awaiting_control = 2;
+ optional InsetsSourceConsumerProto insets_source_consumer = 1;
+ optional .android.view.inputmethod.EditorInfoProto focused_editor = 2;
+ optional bool is_requested_visible_awaiting_control = 3;
} \ No newline at end of file
diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
index 732213966014..f31d35b86a0c 100644
--- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
+++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
@@ -22,7 +22,6 @@ package android.view.inputmethod;
import "frameworks/base/core/proto/android/view/inputmethod/inputmethodmanager.proto";
import "frameworks/base/core/proto/android/view/viewrootimpl.proto";
import "frameworks/base/core/proto/android/view/insetscontroller.proto";
-import "frameworks/base/core/proto/android/view/insetssourceconsumer.proto";
import "frameworks/base/core/proto/android/view/imeinsetssourceconsumer.proto";
import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto";
import "frameworks/base/core/proto/android/view/imefocuscontroller.proto";
@@ -54,14 +53,19 @@ message InputMethodEditorProto {
/* required: elapsed realtime in nanos since boot of when this entry was logged */
optional fixed64 elapsed_realtime_nanos = 1;
- optional ClientSideProto client_side_dump = 2;
+ optional ClientsProto clients = 2;
+
+ // this wrapper helps to simplify the dumping logic
+ message ClientsProto {
+ repeated ClientSideProto client = 1;
+ }
/* groups together the dump from ime related client side classes */
message ClientSideProto {
- optional InputMethodManagerProto input_method_manager = 1;
- optional ViewRootImplProto view_root_impl = 2;
- optional InsetsControllerProto insets_controller = 3;
- optional InsetsSourceConsumerProto insets_source_consumer = 4;
+ optional int32 display_id = 1;
+ optional InputMethodManagerProto input_method_manager = 2;
+ optional ViewRootImplProto view_root_impl = 3;
+ optional InsetsControllerProto insets_controller = 4;
optional ImeInsetsSourceConsumerProto ime_insets_source_consumer = 5;
optional EditorInfoProto editor_info = 6;
optional ImeFocusControllerProto ime_focus_controller = 7;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 34543f13c166..723cceba90bc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4322,6 +4322,10 @@
<permission android:name="android.permission.WRITE_DREAM_STATE"
android:protectionLevel="signature|privileged" />
+ <!-- @hide Allows applications to read whether ambient display is suppressed. -->
+ <permission android:name="android.permission.READ_DREAM_SUPPRESSION"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allow an application to read and write the cache partition.
@hide -->
<permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index b54dfc011735..58d6a8614bb3 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Verhoog volume bo aanbevole vlak?\n\nOm lang tydperke teen hoë volume te luister, kan jou gehoor beskadig."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gebruik toeganklikheidkortpad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Wanneer die kortpad aan is, sal \'n toeganklikheidkenmerk begin word as albei volumeknoppies 3 sekondes lank gedruk word."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Skakel toeganklikheidkenmerke aan?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"As jy albei volumesleutels vir \'n paar sekondes hou, skakel dit toeganklikheidkenmerke aan. Dit kan verander hoe jou toestel werk.\n\nHuidige kenmerke:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nJy kan geselekteerde kenmerke in Instellings en Toeganklikheid verander."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Skakel <xliff:g id="SERVICE">%1$s</xliff:g> aan?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"As jy albei volumesleutels vir \'n paar sekondes hou, skakel dit <xliff:g id="SERVICE">%1$s</xliff:g>, \'n toeganklikheidkenmerk, aan. Dit kan verander hoe jou toestel werk.\n\nJy kan hierdie kortpad na \'n ander kenmerk in Instellings en Toeganklikheid verander."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Skakel aan"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Moenie aanskakel nie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 668ce4232f76..b3878d852b6b 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ድምጹ ከሚመከረው መጠን በላይ ከፍ ይበል?\n\nበከፍተኛ ድምጽ ለረጅም ጊዜ ማዳመጥ ጆሮዎን ሊጎዳው ይችላል።"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"የተደራሽነት አቋራጭ ጥቅም ላይ ይዋል?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"አቋራጩ ሲበራ ሁለቱንም የድምጽ አዝራሮች ለ3 ሰከንዶች ተጭኖ መቆየት የተደራሽነት ባህሪን ያስጀምረዋል።"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"የተደራሽነት ባሕሪያት ይብሩ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ሁለቱንም የድምፅ ቁልፎች ወደ ታች ለጥቂት ሰከንዶች መያዝ የተደራሽነት ባሕሪያትን ያበራል። ይህ የእርስዎ መሣሪያ እንዴት እንደሚሠራ ሊለውጥ ይችላል።\n\nየአሁን ባሕሪያት፦\n<xliff:g id="SERVICE">%1$s</xliff:g>\nበቅንብሮች &gt; ተደራሽነት ውስጥ የተመረጡትን ባሕሪያት መለወጥ ይችላሉ።"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ይብራ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ሁለቱንም የድምፅ ቁልፎች ወደ ታች ለጥቂት ሰከንዶች መያዝ የተደራሽነት ባሕሪያትን <xliff:g id="SERVICE">%1$s</xliff:g> ያበራል። ይህ የእርስዎ መሣሪያ እንዴት እንደሚሠራ ሊለውጥ ይችላል።\n\nበቅንብሮች &gt; ተደራሽነት ውስጥ ወደ ሌላ ባሕሪ ይህን አቋራጭ መለወጥ ይችላሉ።"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"አብራ"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"አታብራ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index fdb351ca52a2..44055e1042e8 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1710,10 +1710,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"هل تريد رفع مستوى الصوت فوق المستوى الموصى به؟\n\nقد يضر سماع صوت عالٍ لفترات طويلة بسمعك."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"هل تريد استخدام اختصار \"سهولة الاستخدام\"؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"عند تفعيل الاختصار، يؤدي الضغط على زرّي التحكّم في مستوى الصوت معًا لمدة 3 ثوانٍ إلى تفعيل إحدى ميزات إمكانية الوصول."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"هل تريد تفعيل ميزات إمكانية الوصول؟"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"يؤدي الضغط مع الاستمرار على كلا مفتاحَي التحكّم في مستوى الصوت لبضع ثوانٍ إلى تفعيل ميزات إمكانية الوصول. قد يؤدي هذا الإجراء إلى تغيير طريقة عمل جهازك.\n\nالميزات الحالية:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nيمكنك تغيير الميزات المحددة في الإعدادات &gt; إمكانية الوصول."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"هل تريد تفعيل <xliff:g id="SERVICE">%1$s</xliff:g>؟"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"‏يؤدي الضغط مع الاستمرار لبضع ثوانٍ على كلا مفتاحَي التحكّم في مستوى الصوت إلى تفعيل <xliff:g id="SERVICE">%1$s</xliff:g> وهي إحدى ميزات إمكانية الوصول. يمكن أن يؤدي هذا الإجراء إلى تغيير كيفية عمل جهازك.\n\nيمكنك تغيير هذا الاختصار لاستخدامه مع ميزة أخرى في الإعدادات &gt; أدوات تمكين الوصول."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"تفعيل"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"عدم التفعيل"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 93ce15fc73b4..e787f239f1f7 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"অনুমোদিত স্তৰতকৈ ওপৰলৈ ভলিউম বঢ়াব নেকি?\n\nদীৰ্ঘ সময়ৰ বাবে উচ্চ ভলিউমত শুনাৰ ফলত শ্ৰৱণ ক্ষমতাৰ ক্ষতি হ\'ব পাৰে।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাট ব্যৱহাৰ কৰেনে?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"শ্বৰ্টকাটটো অন হৈ থকাৰ সময়ত দুয়োটা ভলিউম বুটাম ৩ ছেকেণ্ডৰ বাবে হেঁচি ধৰি ৰাখিলে এটা সাধ্য সুবিধা আৰম্ভ হ’ব।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"সাধ্য-সুবিধাসমূহ অন কৰিবনে?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে সাধ্য-সুবিধাসমূহ অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nবর্তমানৰ সুবিধাসমূহ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nআপুনি ছেটিংসমূহ &gt; সাধ্য-সুবিধাত কিছুমান নিৰ্দিষ্ট সুবিধা সলনি কৰিব পাৰে।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> অন কৰিবনে?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে এটা সাধ্য- সুবিধা <xliff:g id="SERVICE">%1$s</xliff:g> অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nআপুনি ছেটিংসমূহ &gt; সাধ্য-সুবিধাসমূহত এই শ্বৰ্টকাটটো অন্য এটা সুবিধালৈ সলনি কৰিব পাৰে।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"অন কৰক"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"অন নকৰিব"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 01b815b43202..af57cf903656 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Səsin həcmi tövsiyə olunan səviyyədən artıq olsun?\n\nYüksək səsi uzun zaman dinləmək eşitmə qabiliyyətinizə zərər vura bilər."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Əlçatımlılıq Qısayolu istifadə edilsin?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Qısayol aktiv olduqda, hər iki səs düyməsinə 3 saniyə basıb saxlamaqla əlçatımlılıq funksiyası başladılacaq."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Əlçatımlılıq funksiyaları aktiv edilsin?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyaları aktiv olur. Bu, cihazınızın işləmə qaydasını dəyişə bilər.\n\nCari funksiyalar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAyarlar və Əlçatımlılıq bölməsində seçilmiş funksiyaları dəyişə bilərsiniz."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> aktiv edilsin?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyası olan <xliff:g id="SERVICE">%1$s</xliff:g> aktiv olur. Bu, cihazınızın işləmə qaydasını dəyişə bilər.\n\nAyarlar və Əlçatımlılıq bölməsində bu qısayolu başqa bir funksiyata dəyişə bilərsiniz."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktiv edin"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Aktiv etməyin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index e2fc8f1f7862..465e9d22d3a2 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite da pojačate zvuk iznad preporučenog nivoa?\n\nSlušanje glasne muzike duže vreme može da vam ošteti sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li da koristite prečicu za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kada je prečica uključena, pritisnite oba dugmeta za jačinu zvuka da biste pokrenuli funkciju pristupačnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Želite li da uključite funkcije pristupačnosti?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ako zadržite oba tastera za jačinu zvuka par sekundi, uključiće se funkcije pristupačnosti. To može da promeni način rada uređaja.\n\nPostojeće funkcije:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nMožete da promenite izabrane funkcije u odeljku Podešavanja &gt; Pristupačnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Želite li da uključite uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ako zadržite oba tastera za jačinu zvuka par sekundi, uključuje se <xliff:g id="SERVICE">%1$s</xliff:g>, funkcija pristupačnosti. To može da promeni način rada uređaja.\n\nMožete da promenite funkciju na koju se odnosi ova prečica u odeljku Podešavanja &gt; Pristupačnost."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Uključi"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne uključuj"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 45008f2458bb..f0594a16ddcb 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Павялiчыць гук вышэй рэкамендаванага ўзроўню?\n\nДоўгае праслухоўванне музыкi на вялiкай гучнасцi можа пашкодзiць ваш слых."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Выкарыстоўваць камбінацыю хуткага доступу для спецыяльных магчымасцей?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Калі хуткі доступ уключаны, вы можаце націснуць абедзве кнопкі гучнасці і ўтрымліваць іх 3 секунды, каб запусціць функцыю спецыяльных магчымасцей."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Уключыць спецыяльныя магчымасці?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Утрымліванне націснутымі абедзвюх клавіш гучнасці на працягу некалькіх секунд уключае спецыяльныя магчымасці. У выніку ваша прылада можа пачаць працаваць па-іншаму.\n\nБягучыя функцыі:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nВыбраныя функцыі можна змяніць у меню \"Налады &gt; Спецыяльныя магчымасці\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Уключыць службу \"<xliff:g id="SERVICE">%1$s</xliff:g>\"?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Утрымліванне націснутымі абедзвюх клавіш гучнасці на працягу некалькіх секунд уключае службу \"<xliff:g id="SERVICE">%1$s</xliff:g>\", якая з\'яўляецца спецыяльнай магчымасцю. У выніку ваша прылада можа пачаць працаваць па-іншаму.\n\nВы можаце задаць гэта спалучэнне клавіш для іншай функцыі ў меню \"Налады &gt; Спецыяльныя магчымасці\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Уключыць"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не ўключаць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 0c2c0225eab9..99f6072125d8 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Да се увеличи ли силата на звука над препоръчителното ниво?\n\nПродължителното слушане при висока сила на звука може да увреди слуха ви."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Искате ли да използвате пряк път към функцията за достъпност?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Когато прекият път е включен, можете да стартирате дадена функция за достъпност, като натиснете двата бутона за силата на звука и ги задържите за 3 секунди."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Включване на функциите за достъпност?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Натиснете двата бутона за силата на звука и ги задръжте за няколко секунди, за да включите функциите за достъпност. Това може да промени начина, по който работи устройството ви.\n\nТекущи функции:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nМожете да промените избраните функции от „Настройки“ &gt; „Достъпност“."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Да се включи ли <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Натиснете двата бутона за силата на звука и ги задръжте за няколко секунди, за да включите функцията за достъпност <xliff:g id="SERVICE">%1$s</xliff:g>. Това може да промени начина, по който работи устройството ви.\n\nМожете да зададете друга функция за този пряк път от „Настройки“ &gt; „Достъпност“."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Включване"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Без включване"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index b10d8dcad74f..a07a1fe85577 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"প্রস্তাবিত স্তরের চেয়ে বেশি উঁচুতে ভলিউম বাড়াবেন?\n\nউঁচু ভলিউমে বেশি সময় ধরে কিছু শুনলে আপনার শ্রবনশক্তির ক্ষতি হতে পারে।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"অ্যাক্সেসযোগ্যতা শর্টকাট ব্যবহার করবেন?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"শর্টকাট চালু করা থাকাকালীন দুটি ভলিউম বোতাম একসাথে ৩ সেকেন্ড টিপে ধরে রাখলে একটি অ্যাকসেসিবিলিটি ফিচার চালু হবে।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"অ্যাক্সেসিবিলিটি ফিচার চালু করতে চান?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"উভয় ভলিউম কী কয়েক সেকেন্ড ধরে থাকলে অ্যাক্সেসিবিলিটি ফিচার চালু হয়ে যাবে। এর ফলে, আপনার ডিভাইস কীভাবে কাজ করবে সেটিতে পরিবর্তন হতে পারে।\n\nবর্তমান ফিচার:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nসেটিংস &gt; অ্যাক্সেসিবিলিটি বিকল্প থেকে আপনি বাছাই করা ফিচার পরিবর্তন করতে পারবেন।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> চালু করতে চান?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"উভয় ভলিউম কী কয়েক সেকেন্ড ধরে থাকলে <xliff:g id="SERVICE">%1$s</xliff:g> চালু হয়ে যাবে। এটি একটি অ্যাক্সেসিবিলিটি ফিচার। এর ফলে, আপনার ডিভাইস কীভাবে কাজ করবে সেটিতে পরিবর্তন হতে পারে।\n\nসেটিংস &gt; অ্যাক্সেসিবিলিটি থেকে আপনি এই শর্টকাট পরিবর্তন করতে পারবেন।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"চালু করুন"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"চালু করবেন না"</string>
@@ -1829,8 +1831,7 @@
<item quantity="one">%d ঘন্টার জন্য</item>
<item quantity="other">%d ঘন্টার জন্য</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত (পরবর্তী অ্যালার্ম)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 17c0d1daa0fb..1292fc13db07 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite li pojačati zvuk iznad preporučenog nivoa?\n\nDužim slušanjem glasnog zvuka možete oštetiti sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li koristiti Prečicu za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kada je prečica uključena, pritiskom i držanjem oba dugmeta za jačinu zvuka u trajanju od 3 sekunde pokrenut će se funkcija pristupačnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Uključiti funkcije pristupačnosti?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkcije pristupačnosti. Ovo može uticati na način rada uređaja.\n\nTrenutne funkcije:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nOdabrane funkcije možete promijeniti u odjeljku Postavke &gt; Pristupačnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Uključiti <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkciju pristupačnosti <xliff:g id="SERVICE">%1$s</xliff:g>. Ovo može promijeniti način rada uređaja.\n\nOvu prečicu možete zamijeniti drugom funkcijom u odjeljku Postavke &gt; Pristupačnost."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Uključi"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nemoj uključiti"</string>
@@ -1860,7 +1862,7 @@
<item quantity="few">%d sata</item>
<item quantity="other">%d sati</item>
</plurals>
- <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sljedeći alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dok ne isključite"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index e0c709d4e380..28d961bb4b0a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -991,8 +991,8 @@
<string name="searchview_description_submit" msgid="6771060386117334686">"Envia la consulta"</string>
<string name="searchview_description_voice" msgid="42360159504884679">"Cerca per veu"</string>
<string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Vols activar l\'exploració tàctil?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interactuar amb la tauleta."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració per tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interactuar amb el telèfon."</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interaccionar amb la tauleta."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració per tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interaccionar amb el telèfon."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"Fa 1 mes"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Fa més d\'1 mes"</string>
<plurals name="last_num_days" formatted="false" msgid="687443109145393632">
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vols apujar el volum per sobre del nivell recomanat?\n\nSi escoltes música a un volum alt durant períodes llargs, pots danyar-te l\'oïda."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vols fer servir la drecera d\'accessibilitat?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Si la drecera està activada, prem els dos botons de volum durant 3 segons per iniciar una funció d\'accessibilitat."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vols activar les funcions d\'accessibilitat?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si mantens premudes les dues tecles de volum durant uns segons, s\'activaran les funcions d\'accessibilitat. Això podria canviar el funcionament del teu dispositiu.\n\nFuncions actuals:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPots canviar les funcions seleccionades a Configuració &gt; Accessibilitat."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vols activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si mantens premudes les dues tecles de volum durant uns segons, la funció d\'accessibilitat <xliff:g id="SERVICE">%1$s</xliff:g> s\'activarà. Això podria canviar el funcionament del teu dispositiu.\n\nPots canviar la funció d\'aquesta drecera a Configuració &gt; Accessibilitat."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activa"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"No activis"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 095ec69af783..89f679eb896c 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zvýšit hlasitost nad doporučenou úroveň?\n\nDlouhodobý poslech hlasitého zvuku může poškodit sluch."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Použít zkratku přístupnosti?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Když je tato zkratka zapnutá, můžete funkci přístupnosti spustit tím, že na tři sekundy podržíte obě tlačítka hlasitosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Zapnout funkce pro usnadnění přístupu?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Podržením obou tlačítek hlasitosti po dobu několika sekund zapnete funkce pro usnadnění přístupu. Tato funkce může změnit fungování zařízení.\n\nAktuální funkce:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nVybrané funkce můžete změnit v Nastavení &gt; Přístupnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Zapnout <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Podržením obou tlačítek hlasitosti po dobu několika sekund zapnete funkci pro usnadnění přístupu <xliff:g id="SERVICE">%1$s</xliff:g>. Tato funkce může změnit fungování zařízení.\n\nZkratku můžete nastavit na jinou funkci v Nastavení &gt; Přístupnost."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Zapnout"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nezapínat"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 499b9dee0d87..e9d477d7143d 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vil du skrue højere op end det anbefalede lydstyrkeniveau?\n\nDu kan skade hørelsen ved at lytte til meget høj musik over længere tid."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vil du bruge genvejen til Hjælpefunktioner?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Når genvejen er aktiveret, kan du starte en hjælpefunktion ved at trykke på begge lydstyrkeknapper i tre sekunder."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vil du aktivere hjælpefunktionerne?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hvis du holder begge lydstyrkeknapperne nede i et par sekunder, aktiveres hjælpefunktionerne. Det kan ændre på, hvordan din enhed fungerer.\n\nAktuelle funktioner:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kan ændre de valgte funktioner i Indstillinger &gt; Hjælpefunktioner."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vil du aktivere <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hvis du holder begge lydstyrkeknapperne nede i et par sekunder, aktiveres hjælpefunktionen <xliff:g id="SERVICE">%1$s</xliff:g>. Det kan ændre på, hvordan din enhed fungerer.\n\nDu kan ændre denne genvej til en anden funktion i Indstillinger &gt; Hjælpefunktioner."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivér"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Aktivér ikke"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 867efac7df21..682491ec1f3d 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Lautstärke über den Schwellenwert anheben?\n\nWenn du über einen längeren Zeitraum Musik in hoher Lautstärke hörst, kann dies dein Gehör schädigen."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Verknüpfung für Bedienungshilfen verwenden?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Wenn die Verknüpfung aktiviert ist, kannst du die beiden Lautstärketasten drei Sekunden lang gedrückt halten, um eine Bedienungshilfe zu starten."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Bedienungshilfen aktivieren?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Wenn du beide Lautstärketasten einige Sekunden lang gedrückt hältst, aktivierst du die Bedienungshilfen. Dadurch kann sich die Funktionsweise deines Geräts ändern.\n\nAktuelle Funktionen:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kannst ausgewählte Funktionen unter \"Einstellungen\" &gt; \"Bedienungshilfen\" ändern."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> aktivieren?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Wenn du beide Lautstärketasten einige Sekunden lang gedrückt hältst, aktivierst du die Bedienungshilfe \"<xliff:g id="SERVICE">%1$s</xliff:g>\". Dadurch kann sich die Funktionsweise deines Geräts ändern.\n\nUnter \"Einstellungen &gt; \"Bedienungshilfen\" kannst du dieser Verknüpfung eine andere Funktion zuweisen."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivieren"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nicht aktivieren"</string>
@@ -1792,8 +1794,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
- <string name="battery_saver_description" msgid="6794188153647295212">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert zum einen, dass manche Apps im Hintergrund Daten senden oder empfangen, sodass weniger Daten verbraucht werden. Zum anderen werden die Datenzugriffe der gerade aktiven App eingeschränkt, was z. B. dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Datensparmodus aktivieren?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivieren"</string>
@@ -1829,8 +1831,7 @@
<item quantity="other">Für %d h</item>
<item quantity="one">Für 1 h</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nächste Weckzeit)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Bis zur Deaktivierung"</string>
@@ -2002,9 +2003,9 @@
<string name="notification_feedback_indicator" msgid="663476517711323016">"Feedback geben"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Infomitteilung zum Ablaufmodus"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Dein Akku könnte vor der gewöhnlichen Ladezeit leer sein"</string>
- <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Energiesparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
- <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Energiesparmodus"</string>
- <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Energiesparmodus deaktiviert"</string>
+ <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Stromsparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
+ <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Stromsparmodus"</string>
+ <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Stromsparmodus deaktiviert"</string>
<string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Das Smartphone ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
<string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Das Tablet ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
<string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Das Gerät ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index d4d1c5a43374..9449ca067273 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Αυξάνετε την ένταση ήχου πάνω από το επίπεδο ασφαλείας;\n\nΑν ακούτε μουσική σε υψηλή ένταση για μεγάλο χρονικό διάστημα ενδέχεται να προκληθεί βλάβη στην ακοή σας."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Να χρησιμοποιείται η συντόμευση προσβασιμότητας;"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Όταν η συντόμευση είναι ενεργοποιημένη, το πάτημα και των δύο κουμπιών έντασης ήχου για 3 δευτερόλεπτα θα ξεκινήσει μια λειτουργία προσβασιμότητας."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Θέλετε να ενεργοποιήσετε τις λειτουργίες προσβασιμότητας;"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Για να ενεργοποιήσετε τις λειτουργίες προσβασιμότητας, πατήστε παρατεταμένα τα δύο πλήκτρα έντασης για μερικά δευτερόλεπτα. Αυτό ενδέχεται να αλλάξει τον τρόπο λειτουργίας της συσκευής σας.\n\nΤρέχουσες λειτουργίες:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nΜπορείτε να αλλάξετε τις επιλεγμένες λειτουργίες στις Ρυθμίσεις &gt; Προσβασιμότητα."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Θέλετε να ενεργοποιήσετε τη λειτουργία <xliff:g id="SERVICE">%1$s</xliff:g>;"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Μπορείτε να ενεργοποιήσετε τη λειτουργία <xliff:g id="SERVICE">%1$s</xliff:g>, η οποία είναι μία από τις λειτουργίες προσβασιμότητας, πατώντας παρατεταμένα ταυτόχρονα τα δύο πλήκτρα έντασης ήχου για μερικά δευτερόλεπτα. Αυτό ενδέχεται να αλλάξει τον τρόπο λειτουργίας της συσκευής σας.\n\nΜπορείτε να αλλάξετε αυτή τη συντόμευση σε μια άλλη λειτουργία στις Ρυθμίσεις &gt; Προσβασιμότητα."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ενεργοποίηση"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Να μην ενεργοποιηθούν"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index f140ce3fe155..0788a60a9592 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nYou can change selected features in Settings &gt; Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%1$s</xliff:g>, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings &gt; Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index e814bdfbad83..6e841ec66c73 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nYou can change selected features in Settings &gt; Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%1$s</xliff:g>, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings &gt; Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 73f1562e5058..731f1845a7fd 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nYou can change selected features in Settings &gt; Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%1$s</xliff:g>, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings &gt; Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 122ea7fa7386..8bd3597ef538 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nYou can change selected features in Settings &gt; Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%1$s</xliff:g>, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings &gt; Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 53d8c6653461..f9793d2aa6ff 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎Raise volume above recommended level?‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Listening at high volume for long periods may damage your hearing.‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‏‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‏‎‏‎‏‎‏‎Use Accessibility Shortcut?‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎‎‎When the shortcut is on, pressing both volume buttons for 3 seconds will start an accessibility feature.‎‏‎‎‏‎"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎Turn on accessibility features?‎‏‎‎‏‎"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‎‏‎‎Turn on shortcut for accessibility features?‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‎‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‏‎Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Current features:‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="SERVICE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎You can change selected features in Settings &gt; Accessibility.‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎‎‏‏‏‎‏‎‎‏‎‏‏‎‏‏‎ • ‎‏‎‎‏‏‎<xliff:g id="SERVICE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‏‎‎‏‎Turn on ‎‏‎‎‏‏‎<xliff:g id="SERVICE">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‎‎Turn on ‎‏‎‎‏‏‎<xliff:g id="SERVICE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ shortcut?‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎‏‎Holding down both volume keys for a few seconds turns on ‎‏‎‎‏‏‎<xliff:g id="SERVICE">%1$s</xliff:g>‎‏‎‎‏‏‏‎, an accessibility feature. This may change how your device works.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎You can change this shortcut to another feature in Settings &gt; Accessibility.‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎Turn on‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎‏‏‎Don’t turn on‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 953b6fc3a9d7..03530008e4bc 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar a un alto volumen durante largos períodos puede dañar tu audición."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"¿Usar acceso directo de accesibilidad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Cuando la combinación de teclas está activada, puedes presionar los botones de volumen durante 3 segundos para iniciar una función de accesibilidad."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"¿Quieres activar las funciones de accesibilidad?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si mantienes presionadas las dos teclas de volumen durante unos segundos, se activarán las funciones de accesibilidad. Esto puede cambiar el funcionamiento de tu dispositivo.\n\nFunciones actuales:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuedes cambiar las funciones seleccionadas en Configuración &gt; Accesibilidad."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"¿Quieres activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si mantienes presionadas ambas teclas de volumen durante unos segundos, se activará la función de accesibilidad <xliff:g id="SERVICE">%1$s</xliff:g>. Esto podría cambiar la forma en que funciona tu dispositivo.\n\nPuedes cambiar este acceso directo a otra función en Configuración &gt; Accesibilidad."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"No activar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 465d2ec63d3a..e64f5682076e 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar sonidos fuertes durante mucho tiempo puede dañar los oídos."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"¿Utilizar acceso directo de accesibilidad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Si el acceso directo está activado, pulsa los dos botones de volumen durante 3 segundos para iniciar una función de accesibilidad."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"¿Activar funciones de accesibilidad?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Al mantener pulsadas las dos teclas de volumen durante unos segundos, se activan las funciones de accesibilidad, que pueden cambiar el funcionamiento del dispositivo.\n\nFunciones actuales:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuedes cambiar las funciones seleccionadas en Ajustes &gt; Accesibilidad."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"¿Quieres activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Al mantener pulsadas ambas teclas de volumen durante unos segundos se activa <xliff:g id="SERVICE">%1$s</xliff:g>, una función de accesibilidad. Esta función puede modificar el funcionamiento del dispositivo.\n\nPuedes asignar este acceso directo a otra función en Ajustes &gt; Accesibilidad."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"No activar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 6d2a4c0f2bfb..a3622d643305 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Kas suurendada helitugevuse taset üle soovitatud taseme?\n\nPikaajaline valju helitugevusega kuulamine võib kuulmist kahjustada."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Kas kasutada juurdepääsetavuse otseteed?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kui otsetee on sisse lülitatud, käivitab mõlema helitugevuse nupu kolm sekundit all hoidmine juurdepääsetavuse funktsiooni."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Kas lülitada juurdepääsufunktsioonid sisse?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hoidke juurdepääsufunktsioonide sisselülitamiseks mõlemat helitugevuse klahvi mõni sekund all. See võib teie seadme tööviisi muuta.\n\nPraegused funktsioonid:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nValitud funktsioone saab muuta jaotises Seaded &gt; Juurdepääsetavus."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Kas lülitada <xliff:g id="SERVICE">%1$s</xliff:g> sisse?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Kui hoiate mõlemat helitugevuse klahvi mõni sekund all, lülitatakse juurdepääsufunktsioon <xliff:g id="SERVICE">%1$s</xliff:g> sisse. See võib teie seadme tööviisi muuta.\n\nSelle otsetee saab asendada muu otseteega jaotises Seaded &gt; Juurdepääsetavus."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Lülita sisse"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ära lülita sisse"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 7241b974c3c8..47d41f500513 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Bolumena gomendatutako mailatik gora igo nahi duzu?\n\nMusika bolumen handian eta denbora luzez entzuteak entzumena kalte diezazuke."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Erabilerraztasun-lasterbidea erabili nahi duzu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Lasterbidea aktibatuta dagoenean, bi bolumen-botoiak hiru segundoz sakatuta abiaraziko da erabilerraztasun-eginbidea."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Erabilerraztasun-eginbideak aktibatu nahi dituzu?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Eduki sakatuta bolumen-botoiak segundo batzuez erabilerraztasun-eginbideak aktibatzeko. Hori eginez gero, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nUneko eginbideak:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nHautatutako eginbideak aldatzeko, joan Ezarpenak &gt; Erabilerraztasuna atalera."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> aktibatu nahi duzu?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Eduki sakatuta bolumen-botoiak segundo batzuez <xliff:g id="SERVICE">%1$s</xliff:g> izeneko erabilerraztasun-eginbidea aktibatzeko. Honen bidez, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nLasterbide hau beste eginbide batengatik aldatzeko, joan Ezarpenak &gt; Erabilerraztasuna atalera."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktibatu"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ez aktibatu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 02f4728de45e..807a7aed3272 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"میزان صدا را به بالاتر از حد توصیه شده افزایش می‌دهید؟\n\nگوش دادن به صداهای بلند برای مدت طولانی می‌تواند به شنوایی‌تان آسیب وارد کند."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"از میان‌بر دسترس‌پذیری استفاده شود؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"وقتی میان‌بر روشن باشد، با فشار دادن هردو دکمه صدا به‌مدت ۳ ثانیه ویژگی دسترس‌پذیری فعال می‌شود."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ویژگی‌های دسترس‌پذیری روشن شود؟"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"با پایین نگه داشتن هردو کلید میزان صدا به‌مدت چند ثانیه، ویژگی‌های دسترس‌پذیری روشن می‌شود. با این کار نحوه عملکرد دستگاهتان تغییر می‌کند.\n\nویژگی‌های فعلی:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nمی‌توانید ویژگی‌های انتخابی را در «تنظیمات &gt; دسترس‌پذیری» تغییر دهید."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> روشن شود؟"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"با پایین نگه داشتن هردو کلید میزان صدا به‌مدت چند ثانیه، <xliff:g id="SERVICE">%1$s</xliff:g> (یکی از ویژگی‌های دسترس‌پذیری) روشن می‌شود. با این کار نحوه عملکرد دستگاهتان تغییر می‌کند.\n\nمی‌توانید در «تنظیمات &gt; دسترس‌پذیری»،‌این میان‌بر را به ویژگی دیگری تغییر دهید."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"روشن شود"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"روشن نشود"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 0c94f35de91b..da7ab3cf0107 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Nostetaanko äänenvoimakkuus suositellun tason yläpuolelle?\n\nPitkäkestoinen kova äänenvoimakkuus saattaa heikentää kuuloa."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Käytetäänkö esteettömyyden pikanäppäintä?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kun pikanäppäin on käytössä, voit käynnistää esteettömyystoiminnon pitämällä molempia äänenvoimakkuuspainikkeita painettuna kolmen sekunnin ajan."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Laitetaanko esteettömyysominaisuudet päälle?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Molempien äänenvoimakkuuspainikkeiden painaminen muutaman sekunnin ajan laittaa esteettömyysominaisuudet päälle. Tämä voi muuttaa laitteesi toimintaa.\n\nNykyiset ominaisuudet:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nVoit muuttaa valittuja ominaisuuksia kohdassa Asetukset &gt; Esteettömyys."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Laitetaanko <xliff:g id="SERVICE">%1$s</xliff:g> päälle?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Molempien äänenvoimakkuuspainikkeiden pitkään painaminen laittaa päälle esteettömyysominaisuuden <xliff:g id="SERVICE">%1$s</xliff:g>. Tämä voi muuttaa laitteesi toimintaa.\n\nVoit muuttaa tätä pikanäppäintä kohdassa Asetukset &gt; Esteettömyys."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Laita päälle"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Älä laita päälle"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index dcfe8ad100cb..d44f0369c557 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Augmenter le volume au-dessus du niveau recommandé?\n\nL\'écoute prolongée à un volume élevé peut endommager vos facultés auditives."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utiliser le raccourci d\'accessibilité?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quand le raccourci est activé, appuyez sur les deux boutons de volume pendant trois secondes pour lancer une fonctionnalité d\'accessibilité."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Activer les fonctionnalités d\'accessibilité?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si vous maintenez enfoncées les deux touches de volume pendant quelques secondes, vous activez les fonctionnalités d\'accessibilité. Cela peut modifier le fonctionnement de votre appareil.\n\nFonctionnalités actuellement utilisées :\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPour les modifier, sélectionnez Paramètres &gt; Accessibilité."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Activer <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si vous maintenez enfoncées les deux touches de volume pendant quelques secondes, vous activez la fonctionnalité d\'accessibilité <xliff:g id="SERVICE">%1$s</xliff:g>. Cela peut modifier le fonctionnement de votre appareil.\n\nPour attribuer ce raccourci à une autre fonctionnalité, sélectionnez Paramètres &gt; Accessibilité."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activer"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne pas activer"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 5fc2d6918a19..57040d057529 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Augmenter le volume au dessus du niveau recommandé ?\n\nL\'écoute prolongée à un volume élevé peut endommager vos facultés auditives."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utiliser le raccourci d\'accessibilité ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quand le raccourci est activé, appuyez sur les deux boutons de volume pendant trois secondes pour démarrer une fonctionnalité d\'accessibilité."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Activer les fonctionnalités d\'accessibilité ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si vous appuyez sur les deux touches de volume pendant quelques secondes, vous activez des fonctionnalités d\'accessibilité. Cela peut affecter le fonctionnement de votre appareil.\n\nFonctionnalités actuellement utilisées :\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPour les modifier, accédez à Paramètres &gt; Accessibilité."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Activer <xliff:g id="SERVICE">%1$s</xliff:g> ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si vous appuyez sur les deux touches de volume pendant quelques secondes, vous activez la fonctionnalité d\'accessibilité <xliff:g id="SERVICE">%1$s</xliff:g>. Cela peut affecter le fonctionnement de votre appareil.\n\nPour attribuer ce raccourci à une autre fonctionnalité, accédez à Paramètres &gt; Accessibilité."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activer"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne pas activer"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index a6eac55b8caa..581dda5c8afc 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Queres subir o volume máis do nivel recomendado?\n\nA reprodución de son a un volume elevado durante moito tempo pode provocar danos nos oídos."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Queres utilizar o atallo de accesibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Cando o atallo está activado, podes premer os dous botóns de volume durante 3 segundos para iniciar unha función de accesibilidade."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Queres activar as funcións de accesibilidade?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ao manter as dúas teclas de volume premidas durante uns segundos actívanse as funcións de accesibilidade. Esta acción pode cambiar o funcionamento do dispositivo.\n\nFuncións activadas actualmente:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPodes cambiar as funcións seleccionadas en Configuración &gt; Accesibilidade."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Queres activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ao manter as dúas teclas de volume premidas durante uns segundos actívase <xliff:g id="SERVICE">%1$s</xliff:g>, unha función de accesibilidade. Esta acción pode cambiar o funcionamento do dispositivo.\n\nPodes cambiar o uso deste atallo para outra función en Configuración &gt; Accesibilidade."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Non activar"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 6b4bbf36a447..c95b8ebfbc17 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ભલામણ કરેલ સ્તરની ઉપર વૉલ્યૂમ વધાર્યો?\n\nલાંબા સમય સુધી ઊંચા અવાજે સાંભળવું તમારી શ્રવણક્ષમતાને નુકસાન પહોંચાડી શકે છે."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ઍક્સેસિબિલિટી શૉર્ટકટનો ઉપયોગ કરીએ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"જ્યારે શૉર્ટકટ ચાલુ હોય, ત્યારે બન્ને વૉલ્યૂમ બટનને 3 સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધા શરૂ થઈ જશે."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ઍક્સેસિબિલિટી સુવિધાઓ ચાલુ કરીએ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"બન્ને વૉલ્યૂમ કીને થોડી સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધાઓ ચાલુ થઈ જાય છે. આનાથી તમારા ડિવાઇસની કામ કરવાની રીત બદલાઈ શકે છે.\n\nવર્તમાન સુવિધાઓ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nતમે સેટિંગ &gt; ઍક્સેસિબિલિટીમાં જઈને પસંદ કરેલી સુવિધાઓને બદલી શકો છો."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g>ને ચાલુ કરીએ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"બન્ને વૉલ્યૂમ કીને થોડી સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધા એવી <xliff:g id="SERVICE">%1$s</xliff:g> ચાલુ થઈ જાય છે. આનાથી તમારા ડિવાઇસની કામ કરવાની રીત બદલાઈ શકે છે.\n\nતમે સેટિંગ &gt; ઍક્સેસિબિલિટીમાં જઈને આ શૉર્ટકટને બીજી સુવિધામાં બદલી શકો છો."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ચાલુ કરો"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ચાલુ કરશો નહીં"</string>
@@ -1829,8 +1831,7 @@
<item quantity="one">%d કલાક માટે</item>
<item quantity="other">%d કલાક માટે</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> સુધી"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> સુધી"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (આગલા એલાર્મ) સુધી"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"તમે બંધ ન કરો ત્યાં સુધી"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 3bf9dcd90ee2..b56fa2c4caf4 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"वॉल्यूम को सुझाए गए स्तर से ऊपर बढ़ाएं?\n\nअत्यधिक वॉल्यूम पर ज़्यादा समय तक सुनने से आपकी सुनने की क्षमता को नुकसान हो सकता है."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"सुलभता शॉर्टकट का इस्तेमाल करना चाहते हैं?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"शॉर्टकट के चालू होने पर, दाेनाें वॉल्यूम बटन (आवाज़ कम या ज़्यादा करने वाले बटन) को तीन सेकंड तक दबाने से, सुलभता सुविधा शुरू हाे जाएगी."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"सुलभता सुविधाएं चालू करना चाहते हैं?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"आवाज़ कम और ज़्यादा करने वाले दोनों बटन को कुछ सेकंड तक दबाकर रखने से सुलभता सुविधाएं चालू हो जाती हैं. ऐसा करने से आपके डिवाइस के काम करने के तरीके में बदलाव हो सकता है.\n\nमौजूदा सुविधाएं:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nआप सेटिंग और सुलभता में जाकर चुनी हुई सुविधाएं बदल सकते हैं."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> चालू करना चाहते हैं?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"आवाज़ कम और ज़्यादा करने वाले दोनों बटन को कुछ सेकंड तक दबाकर रखने से <xliff:g id="SERVICE">%1$s</xliff:g> चालू हो जाती है, जो एक सुलभता सुविधा है. ऐसा करने से आपके डिवाइस के काम करने के तरीके में बदलाव हो सकता है.\n\nआप सेटिंग और सुलभता में जाकर इस शॉर्टकट को किसी दूसरी सुविधा के लिए बदल सकते हैं."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"चालू करें"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"चालू न करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index d350d04b15f4..2678cb863a7a 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite li pojačati zvuk iznad preporučene razine?\n\nDugotrajno slušanje glasne glazbe može vam oštetiti sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li upotrebljavati prečac za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kad je taj prečac uključen, pritiskom na obje tipke za glasnoću na tri sekunde pokrenut će se značajka pristupačnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Želite li uključiti značajke pristupačnosti?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Značajke pristupačnosti uključuju se ako na nekoliko sekundi pritisnete obje tipke za glasnoću. Time se može promijeniti način na koji vaš uređaj radi.\n\nTrenutačne značajke:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nOdabrane značajke možete promijeniti u odjeljku Postavke &gt; Pristupačnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Želite li uključiti uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ako na nekoliko sekundi pritisnete obje tipke za glasnoću, uključuje se značajka pristupačnosti <xliff:g id="SERVICE">%1$s</xliff:g>. Time se može promijeniti način na koji vaš uređaj radi.\n\nZnačajku na koju se taj prečac odnosi možete promijeniti u odjeljku Postavke &gt; Pristupačnost."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Uključi"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nemoj uključiti"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 70f87b689edc..235c8fd77999 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Az ajánlott szint fölé szeretné emelni a hangerőt?\n\nHa hosszú időn át teszi ki magát nagy hangerőnek, azzal károsíthatja a hallását."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Szeretné használni a Kisegítő lehetőségek billentyűparancsot?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Ha a gyorsparancs aktív, akkor a két hangerőgomb három másodpercig tartó együttes lenyomásával kisegítő funkciót indíthat el."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Bekapcsolja a kisegítő lehetőségeket?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"A kisegítő lehetőségek bekapcsolásához tartsa nyomva néhány másodpercig mindkét hangerőgombot. Ez hatással lehet az eszköz működésére.\n\nJelenlegi funkciók:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nA kiválasztott funkciókat a Beállítások &gt; Kisegítő lehetőségek pontban módosíthatja."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Bekapcsolja a következőt: <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"A(z) <xliff:g id="SERVICE">%1$s</xliff:g> kisegítő lehetőség bekapcsolásához tartsa nyomva néhány másodpercig mindkét hangerőgombot. Ez hatással lehet az eszköz működésére.\n\nEzt a gyorsparancsot a Beállítások &gt; Kisegítő lehetőségek pontban módosíthatja másik funkció használatára."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Bekapcsolom"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nem kapcsolom be"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 32ac6a8beb2a..3634908d6111 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ձայնը բարձրացնե՞լ խորհուրդ տրվող մակարդակից ավել:\n\nԵրկարատև բարձրաձայն լսելը կարող է վնասել ձեր լսողությունը:"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Օգտագործե՞լ Մատչելիության դյուրանցումը։"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Հատուկ գործառույթն օգտագործելու համար սեղմեք և 3 վայրկյան սեղմած պահեք ձայնի ուժգնության երկու կոճակները, երբ գործառույթը միացված է:"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Միացնե՞լ հատուկ գործառույթները"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ձայնի կարգավորման երկու կոճակները մի քանի վայրկյան սեղմած պահելով կմիացնեք հատուկ գործառույթները։ Դրա արդյունքում սարքի աշխատաեղանակը կարող է փոխվել։\n\nԸնթացիկ գործառույթներ՝\n<xliff:g id="SERVICE">%1$s</xliff:g>\nԸնտրված գործառույթները փոխելու համար անցեք Կարգավորումներ &gt; Հատուկ գործառույթներ։"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Միացնե՞լ <xliff:g id="SERVICE">%1$s</xliff:g> ծառայությունը"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ձայնի կարգավորման երկու կոճակները մի քանի վայրկյան սեղմած պահելով կմիացնեք <xliff:g id="SERVICE">%1$s</xliff:g> ծառայությունը, որը հատուկ գործառույթ է։ Դրա արդյունքում սարքի աշխատաեղանակը կարող է փոխվել։\n\nԱյս դյուրանցումը մեկ այլ գործառույթով փոխելու համար անցեք Կարգավորումներ &gt; Հատուկ գործառույթներ։"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Միացնել"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Չմիացնել"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f8ba016e1530..5974594d580c 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Mengeraskan volume di atas tingkat yang disarankan?\n\nMendengarkan dengan volume keras dalam waktu yang lama dapat merusak pendengaran Anda."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gunakan Pintasan Aksesibilitas?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Saat pintasan aktif, menekan kedua tombol volume selama 3 detik akan memulai fitur aksesibilitas."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Aktifkan fitur aksesibilitas?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Menahan kedua tombol volume selama beberapa detik akan mengaktifkan fitur aksesibilitas. Tindakan ini dapat mengubah cara kerja perangkat Anda.\n\nFitur saat ini:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAnda dapat mengubah fitur yang dipilih di Setelan &gt; Aksesibilitas."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Aktifkan <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Menahan kedua tombol volume selama beberapa detik akan mengaktifkan <xliff:g id="SERVICE">%1$s</xliff:g>, yang merupakan fitur aksesibilitas. Tindakan ini dapat mengubah cara kerja perangkat Anda.\n\nAnda dapat mengubah pintasan ini ke fitur lain di Setelan &gt; Aksesibilitas."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktifkan"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Jangan aktifkan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 9278bbb5f595..64e855ff7210 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Hækka hljóðstyrk umfram ráðlagðan styrk?\n\nEf hlustað er á háum hljóðstyrk í langan tíma kann það að skaða heyrnina."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Viltu nota aðgengisflýtileið?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Þegar flýtileiðin er virk er kveikt á aðgengiseiginleikanum með því að halda báðum hljóðstyrkshnöppunum inni í þrjár sekúndur."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Viltu kveikja á aðgengiseiginleikum?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Kveikt er á aðgengiseiginleikum þegar báðum hljóðstyrkstökkunum er haldið inni í nokkrar sekúndur. Þetta getur breytt því hvernig tækið virkar.\n\nNúverandi eiginleikar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nÞú getur breytt völdum eiginleikum í Stillingar &gt; Aðgengi."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Viltu kveikja á <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ef báðum hljóðstyrkstökkunum er haldið inni í nokkrar sekúndur er kveikt á aðgengiseiginleikanum <xliff:g id="SERVICE">%1$s</xliff:g>. Þetta getur breytt því hvernig tækið virkar.\n\nÞú getur breytt þessari flýtileið í annan eiginleika í Stillingar &gt; Aðgengi."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Kveikja"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ekki kveikja"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index b962d8b7c3c8..185bdcfe8957 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vuoi aumentare il volume oltre il livello consigliato?\n\nL\'ascolto ad alto volume per lunghi periodi di tempo potrebbe danneggiare l\'udito."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usare la scorciatoia Accessibilità?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando la scorciatoia è attiva, puoi premere entrambi i pulsanti del volume per tre secondi per avviare una funzione di accessibilità."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Attivare le funzioni di accessibilità?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Se tieni premuti entrambi i tasti del volume per qualche secondo, vengono attivate le funzioni di accessibilità. Questa operazione potrebbe modificare il funzionamento del dispositivo.\n\nFunzioni correnti:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuoi modificare le funzioni selezionate in Impostazioni &gt; Accessibilità."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Attivare <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Se tieni premuti entrambi i tasti del volume per qualche secondo verrà attivata la funzione di accessibilità <xliff:g id="SERVICE">%1$s</xliff:g>. Questa operazione potrebbe modificare il funzionamento del dispositivo.\n\nPuoi associare questa scorciatoia a un\'altra funzionalità in Impostazioni &gt; Accessibilità."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Attiva"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Non attivare"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 821e4fd0acbf..963489e90557 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"האם להעלות את עוצמת הקול מעל לרמה המומלצת?\n\nהאזנה בעוצמת קול גבוהה למשכי זמן ממושכים עלולה לפגוע בשמיעה."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"להשתמש בקיצור הדרך לתכונת הנגישות?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"כשקיצור הדרך מופעל, לחיצה על שני לחצני עוצמת הקול למשך שלוש שניות מפעילה את תכונת הנגישות."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"להפעיל את תכונות הנגישות?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"‏לחיצה ארוכה על שני לחצני עוצמת הקול למשך מספר שניות מפעילה את תכונות הנגישות. בעקבות זאת, ייתכן שאופן הפעולה של המכשיר ישתנה.\n\nהתכונות הנוכחיות:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nניתן לשנות תכונות נבחרות ב\'הגדרות\' &gt; \'נגישות\'."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"להפעיל את <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"‏ניתן ללחוץ על שני מקשי עוצמת הקול למשך מספר שניות כדי להפעיל את <xliff:g id="SERVICE">%1$s</xliff:g>, תכונת נגישות. בעקבות זאת, ייתכן שאופן הפעולה של המכשיר ישתנה.\n\nאפשר לשנות את מקשי הקיצור האלה לתכונה נוספת ב\'הגדרות\' &gt; \'נגישות\'."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"אני רוצה להפעיל"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"לא להפעיל"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 4b48495a562e..ee9f0ebe87d6 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"推奨レベルを超えるまで音量を上げますか?\n\n大音量で長時間聞き続けると、聴力を損なう恐れがあります。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ユーザー補助機能のショートカットの使用"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ショートカットが ON の場合、両方の音量ボタンを 3 秒ほど長押しするとユーザー補助機能が起動します。"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ユーザー補助機能を ON にしますか?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"音量大と音量小の両方のボタンを数秒ほど長押しすると、ユーザー補助機能が ON になります。この機能が ON になると、デバイスの動作が変わることがあります。\n\n現在の機能:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n選択した機能は [設定] &gt; [ユーザー補助] で変更できます。"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> を ON にしますか?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"音量大と音量小の両方のボタンを数秒ほど長押しすると、ユーザー補助機能の <xliff:g id="SERVICE">%1$s</xliff:g> が ON になります。この機能が ON になると、デバイスの動作が変わることがあります。\n\nこのショートカットは [設定] &gt; [ユーザー補助] で別の機能に変更できます。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ON にする"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ON にしない"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index cdac64757058..72d9374008c7 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"გსურთ ხმის რეკომენდებულ დონეზე მაღლა აწევა?\n\nხანგრძლივად ხმამაღლა მოსმენით შესაძლოა სმენადობა დაიზიანოთ."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"გსურთ მარტივი წვდომის მალსახმობის გამოყენება?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"თუ მალსახმობი ჩართულია, ხმის ორივე ღილაკზე 3 წამის განმავლობაში დაჭერით მარტივი წვდომის ფუნქცია ჩაირთვება."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"გსურთ, ჩართოთ მარტივი წვდომის ფუნქციები?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ხმის ორივე ღილაკზე ხანგრძლივად დაჭერა რამდენიმე წამის განმავლობაში ჩართავს მარტივი წვდომის ფუნქციებს. ამ ქმედებამ შეიძლება შეცვალოს თქვენი მოწყობილობის მუშაობის პრინციპი.\n\nამჟამინდელი ფუნქციები:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nარჩეული ფუნქციების შეცვლა შესაძლებელია აქ: პარამეტრები &gt; მარტივი წვდომა."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"ჩაირთოს <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ხმის ორივე ღილაკზე რამდენიმე წამის განმავლობაში დაჭერით ჩაირთვება <xliff:g id="SERVICE">%1$s</xliff:g>, რომელიც მარტივი წვდომის ფუნქციაა. ამან შეიძლება შეცვალოს თქვენი მოწყობილობის მუშაობის პრინციპი.\n\nამ მალსახმობის შეცვლა სხვა ფუნქციით შეგიძლიათ აქ: პარამეტრები &gt; აპები."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ჩართვა"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"არ ჩაირთოს"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 608ed1e0186b..ca1500def455 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Дыбыс деңгейін ұсынылған деңгейден көтеру керек пе?\n\nЖоғары дыбыс деңгейінде ұзақ кезеңдер бойы тыңдау есту қабілетіңізге зиян тигізуі мүмкін."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Арнайы мүмкіндік төте жолын пайдалану керек пе?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Түймелер тіркесімі қосулы кезде, екі дыбыс түймесін 3 секунд басып тұрсаңыз, \"Арнайы мүмкіндіктер\" функциясы іске қосылады."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Арнайы мүмкіндіктер іске қосылсын ба?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, арнайы мүмкіндіктер іске қосылады. Бұл – құрылғының жұмысына әсер етуі мүмкін.\n\nҚазіргі функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТаңдалған функцияларды \"Параметрлер &gt; Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> қосылсын ба?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, <xliff:g id="SERVICE">%1$s</xliff:g> арнайы қызметі іске қосылады. Бұл – құрылғының жүмысына әсер етуі мүмкін.\n\nБұл таңбашаны басқа функцияға \"Параметрлер &gt; Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Қосылсын"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Қосылмасын"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 14b0189a4aa0..44244264b354 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"បង្កើន​កម្រិត​សំឡេង​លើស​ពី​កម្រិត​បាន​ផ្ដល់​យោបល់?\n\nការ​ស្ដាប់​នៅ​កម្រិត​សំឡេង​ខ្លាំង​យូរ​អាច​ធ្វើឲ្យ​ខូច​ត្រចៀក។"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ប្រើប្រាស់​ផ្លូវកាត់​ភាព​ងាយស្រួល?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"នៅពេលបើក​ផ្លូវកាត់ ការចុច​ប៊ូតុង​កម្រិតសំឡេង​ទាំងពីរ​រយៈពេល 3 វិនាទី​នឹង​ចាប់ផ្តើម​មុខងារ​ភាពងាយប្រើ។"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"បើក​មុខងារ​ភាពងាយប្រើ​ឬ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ការសង្កត់​គ្រាប់ចុច​កម្រិតសំឡេង​ទាំងពីរ​ឱ្យជាប់​រយៈពេល​ពីរបីវិនាទី​នឹងបើក​មុខងារ​ភាពងាយប្រើ។ ការធ្វើ​បែបនេះ​អាចផ្លាស់ប្ដូរ​របៀបដែល​ឧបករណ៍​របស់អ្នក​ដំណើរការ។\n\nមុខងារ​បច្ចុប្បន្ន៖\n<xliff:g id="SERVICE">%1$s</xliff:g>\nអ្នកអាច​ប្ដូរ​មុខងារ​ដែលបាន​ជ្រើសរើស​នៅក្នុង​ការកំណត់ &gt; ភាពងាយស្រួល។"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"បើក <xliff:g id="SERVICE">%1$s</xliff:g> ឬ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ការសង្កត់​គ្រាប់ចុច​កម្រិតសំឡេង​ទាំងពីរ​ឱ្យជាប់​រយៈពេល​ពីរបីវិនាទី​នឹងបើក <xliff:g id="SERVICE">%1$s</xliff:g> ដែលជា​មុខងារ​ភាពងាយប្រើ។ ការធ្វើ​បែបនេះ​អាចផ្លាស់ប្ដូរ​របៀបដែល​ឧបករណ៍​របស់អ្នក​ដំណើរការ។\n\nអ្នកអាច​ប្ដូរផ្លូវកាត់​នេះទៅ​មុខងារ​ផ្សេងទៀត​នៅក្នុង​ការកំណត់ &gt; ភាពងាយស្រួល។"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"បើក"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"កុំបើក"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 0fe1c24bddf0..4e2e7e086152 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ವಾಲ್ಯೂಮ್‌ ಅನ್ನು ಶಿಫಾರಸು ಮಾಡಲಾದ ಮಟ್ಟಕ್ಕಿಂತಲೂ ಹೆಚ್ಚು ಮಾಡುವುದೇ?\n\nದೀರ್ಘ ಅವಧಿಯವರೆಗೆ ಹೆಚ್ಚಿನ ವಾಲ್ಯೂಮ್‌ನಲ್ಲಿ ಆಲಿಸುವುದರಿಂದ ನಿಮ್ಮ ಆಲಿಸುವಿಕೆ ಸಾಮರ್ಥ್ಯಕ್ಕೆ ಹಾನಿಯುಂಟು ಮಾಡಬಹುದು."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್‌ಕಟ್ ಬಳಸುವುದೇ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ಶಾರ್ಟ್‌ಕಟ್ ಆನ್ ಆಗಿರುವಾಗ, ಎರಡೂ ವಾಲ್ಯೂಮ್ ಬಟನ್‌ಗಳನ್ನು 3 ಸೆಕೆಂಡುಗಳ ಕಾಲ ಒತ್ತಿದರೆ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯವೊಂದು ಪ್ರಾರಂಭವಾಗುತ್ತದೆ."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆನ್ ಮಾಡಬೇಕೇ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ಎರಡೂ ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಕೆಲವು ಸೆಕೆಂಡುಗಳ ಕಾಲ ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳುವುದರಿಂದ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳು ಆನ್ ಆಗುತ್ತವೆ. ಇದು ನಿಮ್ಮ ಸಾಧನವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ಬದಲಾಯಿಸಬಹುದು.\n\n ಪ್ರಸ್ತುತ ವೈಶಿಷ್ಟ್ಯಗಳು:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿಯಲ್ಲಿ ಆಯ್ದ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ನೀವು ಬದಲಾಯಿಸಬಹುದು."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ಅನ್ನು ಆನ್‌ ಮಾಡುವುದೇ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ಎರಡೂ ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಕೆಲವು ಸೆಕೆಂಡುಗಳ ಕಾಲ ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳುವುದರಿಂದ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯವಾದ <xliff:g id="SERVICE">%1$s</xliff:g> ಆನ್ ಆಗುತ್ತದೆ. ಇದು ನಿಮ್ಮ ಸಾಧನವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ಬದಲಾಯಿಸಬಹುದು.\n\nನೀವು ಈ ಶಾರ್ಟ್‌ಕಟ್ ಅನ್ನು ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಅಕ್ಸೆಸಿಬಿಲಿಟಿಯಲ್ಲಿನ ಮತ್ತೊಂದು ವೈಶಿಷ್ಟ್ಯಕ್ಕೆ ಬದಲಾಯಿಸಬಹುದು."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ಆನ್ ಮಾಡಿ"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ಆನ್ ಮಾಡಬೇಡಿ"</string>
@@ -1829,8 +1831,7 @@
<item quantity="one">%d ಗಂಟೆಗೆ</item>
<item quantity="other">%d ಗಂಟೆಗೆ</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ (ಮುಂದಿನ ಅಲಾರಮ್)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index e3406c1bf003..43c785af7d43 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"권장 수준 이상으로 볼륨을 높이시겠습니까?\n\n높은 볼륨으로 장시간 청취하면 청력에 손상이 올 수 있습니다."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"접근성 단축키를 사용하시겠습니까?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"단축키가 사용 설정된 경우 볼륨 버튼 두 개를 동시에 3초간 누르면 접근성 기능이 시작됩니다."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"접근성 기능을 사용하시겠습니까?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"볼륨 키 2개를 몇 초 동안 길게 누르면 접근성 기능이 사용 설정됩니다. 이때 기기 작동 방식이 달라질 수 있습니다.\n\n현재 기능:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n설정 &gt; 접근성에서 선택한 기능을 변경할 수 있습니다."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g>을(를) 사용하시겠습니까?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"볼륨 키 2개를 몇 초 동안 길게 누르면 <xliff:g id="SERVICE">%1$s</xliff:g> 접근성 기능이 사용 설정됩니다. 이렇게 되면 기기 작동 방식이 달라질 수 있습니다.\n\n설정 &gt; 접근성에서 이 단축키를 다른 기능으로 변경할 수 있습니다."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"사용"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"사용 안 함"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 3268d16c1df9..182af1b1654f 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Сунушталган деңгээлден да катуулатып уккуңуз келеби?\n\nМузыканы узакка чейин катуу уксаңыз, угууңуз начарлап кетиши мүмкүн."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ыкчам иштетесизби?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Атайын мүмкүнчүлүктөрдү иштетесизби?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Атайын мүмкүнчүлүктөр функциясын иштетүү үчүн, үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nУчурдагы функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТандалган функцияларды өзгөртүү үчүн, Жөндөөлөр &gt; Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> күйгүзүлсүнбү?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"<xliff:g id="SERVICE">%1$s</xliff:g> кызматын иштетүү үчүн, үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nБаскычтардын ушул айкалышын башка функцияга дайындоо үчүн, Жөндөөлөр &gt; Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ооба"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Жок"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index d22ce0c1f964..9f964eb26fd8 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ເພີ່ມ​ລະ​ດັບ​ສຽງ​ໃຫ້​ເກີນກວ່າ​ລະ​ດັບ​ທີ່​ແນະ​ນຳ​ບໍ?\n\n​ການ​ຮັບ​ຟັງ​ສຽງ​ໃນ​ລະ​ດັບ​ທີ່​ສູງ​ເປັນ​ໄລ​ຍະ​ເວ​ລາ​ດົນ​​ອາດ​ເຮັດ​ໃຫ້​ການ​ຟັງ​ຂອງ​ທ່ານ​ມີ​ບັນ​ຫາ​ໄດ້."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ໃຊ້ປຸ່ມລັດການຊ່ວຍເຂົ້າເຖິງບໍ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ເມື່ອເປີດໃຊ້ທາງລັດແລ້ວ, ການກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ 3 ວິນາທີຈະເປັນການເລີ່ມຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ເປີດໃຊ້ຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງບໍ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ສອງສາມວິນາທີເພື່ອເປີດໃຊ້ຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ. ນີ້ອາດປ່ຽນວິທີການເຮັດວຽກຂອງອຸປະກອນທ່ານ.\n\nຄຸນສົມບັດປັດຈຸບັນ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nທ່ານສາມາດປ່ຽນຄຸນສົມບັດທີ່ເລືອກໄດ້ໃນການຕັ້ງຄ່າ &gt; ການຊ່ວຍເຂົ້າເຖິງ."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"ເປີດໃຊ້ <xliff:g id="SERVICE">%1$s</xliff:g> ບໍ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ສອງສາມວິນາທີເພື່ອເປີດໃຊ້ <xliff:g id="SERVICE">%1$s</xliff:g>, ຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ. ນີ້ອາດປ່ຽນວິທີການເຮັດວຽກຂອງອຸປະກອນທ່ານ.\n\nທ່ານສາມາດປ່ຽນທາງລັດນີ້ເປັນຄຸນສົມບັດອື່ນໄດ້ໃນການຕັ້ງຄ່າ &gt; ການຊ່ວຍເຂົ້າເຖິງ."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ເປີດໃຊ້"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ບໍ່ເປີດ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ff687c575396..337b31c86328 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Padidinti garsą daugiau nei rekomenduojamas lygis?\n\nIlgai klausydami dideliu garsu galite pažeisti klausą."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Naudoti spartųjį pritaikymo neįgaliesiems klavišą?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kai spartusis klavišas įjungtas, paspaudus abu garsumo mygtukus ir palaikius 3 sekundes bus įjungta pritaikymo neįgaliesiems funkcija."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Įjungti pritaikymo neįgaliesiems funkcijas?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Paspaudus abu garsumo klavišus ir palaikius kelias sekundes įjungiamos pritaikymo neįgaliesiems funkcijos. Tai gali pakeisti įrenginio veikimą.\n\nDabartinės funkcijos:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPasirinktas funkcijas galite pakeisti skiltyje „Nustatymai“ &gt; „Pritaikomumas“."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Įjungti „<xliff:g id="SERVICE">%1$s</xliff:g>“?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Paspaudus abu garsumo klavišus ir palaikius kelias sekundes įjungiama pritaikymo neįgaliesiems funkcija „<xliff:g id="SERVICE">%1$s</xliff:g>“. Tai gali pakeisti įrenginio veikimą.\n\nGalite pakeisti šį spartųjį klavišą į kitą funkciją skiltyje „Nustatymai“ &gt; „Pritaikomumas“."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Įjungti"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Neįjungti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 2cb6058c1ef7..2b92b4425dab 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vai palielināt skaļumu virs ieteicamā līmeņa?\n\nIlgstoši klausoties skaņu lielā skaļumā, var tikt bojāta dzirde."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vai izmantot pieejamības saīsni?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kad īsinājumtaustiņš ir ieslēgts, nospiežot abas skaļuma pogas un 3 sekundes turot tās, tiks aktivizēta pieejamības funkcija."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vai ieslēgt pieejamības funkcijas?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Turot nospiestus abus skaļuma taustiņus dažas sekundes, tiek ieslēgtas pieejamības funkcijas. Tas var mainīt ierīces darbību.\n\nPašreizējās funkcijas:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAtlasītās funkcijas varat mainīt šeit: Iestatījumi &gt; Pieejamība."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vai ieslēgt pakalpojumu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Turot nospiestus abus skaļuma taustiņus dažas sekundes, tiek ieslēgta pieejamības funkcija <xliff:g id="SERVICE">%1$s</xliff:g>. Tas var mainīt ierīces darbību.\n\nŠo saīsni uz citu funkciju varat mainīt šeit: Iestatījumi &gt; Pieejamība."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ieslēgt"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Neieslēgt"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index bf7220c7586e..ccd1ef794ad8 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Да го зголемиме звукот над препорачаното ниво?\n\nСлушањето звуци со голема јачина подолги периоди може да ви го оштети сетилото за слух."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Да се користи кратенка за „Пристапност“?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Кога е вклучена кратенката, ако ги притиснете двете копчиња за јачина на звук во времетраење од 3 секунди, ќе се стартува функција за пристапност."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Да се вклучат функциите за пристапност?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ако ги задржите притиснати двете копчиња за јачина на звук неколку секунди, ќе се вклучат функциите за пристапност. Ова може да го промени начинот на функционирање на уредот.\n\nТековни функции:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nМоже да ги промените избраните функции во „Поставки &gt; Пристапност“."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Да се вклучи <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ако ги задржите притиснати двете копчиња за јачина на звук неколку секунди, ќе се вклучи функцијата за пристапност <xliff:g id="SERVICE">%1$s</xliff:g>. Ова може да го промени начинот на функционирање на уредот.\n\nМоже да ја измените кратенкава да биде за друга функција во „Поставки &gt; Пристапност“."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Вклучи"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не вклучувај"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 513ed1d6c2e6..8ec1c6897712 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"മുകളിൽക്കൊടുത്തിരിക്കുന്ന ശുപാർശചെയ്‌ത ലെവലിലേക്ക് വോളിയം വർദ്ധിപ്പിക്കണോ?\n\nഉയർന്ന വോളിയത്തിൽ ദീർഘനേരം കേൾക്കുന്നത് നിങ്ങളുടെ ശ്രവണ ശേഷിയെ ദോഷകരമായി ബാധിക്കാം."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ഉപയോഗസഹായി കുറുക്കുവഴി ഉപയോഗിക്കണോ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"കുറുക്കുവഴി ഓണായിരിക്കുമ്പോൾ, രണ്ട് വോളിയം ബട്ടണുകളും 3 സെക്കൻഡ് നേരത്തേക്ക് അമർത്തുന്നത് ഉപയോഗസഹായി ഫീച്ചർ ആരംഭിക്കും."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ഉപയോഗസഹായി ഫീച്ചറുകൾ ഓണാക്കണോ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"രണ്ട് വോളിയം കീകളും അൽപ്പ നേരത്തേക്ക് അമർത്തിപ്പിടിക്കുന്നത്, ഉപയോഗസഹായി ഫീച്ചറുകൾ ഓണാക്കുന്നു. നിങ്ങളുടെ ഉപകരണം പ്രവർത്തിക്കുന്ന രീതിയെ ഇത് മാറ്റിയേക്കാം.\n\nനിലവിലുള്ള ഫീച്ചറുകൾ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nതിരഞ്ഞെടുത്ത ഫീച്ചറുകൾ ക്രമീകരണം &gt; ഉപയോഗസഹായി എന്നതിൽ മാറ്റാനാവും."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ഓണാക്കണോ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"രണ്ട് വോളിയം കീകളും അൽപ്പ നേരത്തേക്ക് അമർത്തിപ്പിടിക്കുന്നത് ഉപയോഗസഹായി ഫീച്ചറായ <xliff:g id="SERVICE">%1$s</xliff:g> എന്നതിനെ ഓണാക്കുന്നു. നിങ്ങളുടെ ഉപകരണം പ്രവർത്തിക്കുന്ന വിധം ഇത് മാറ്റിയേക്കാം.\n\nക്രമീകരണം &gt; ഉപയോഗസഹായി എന്നതിലെ മറ്റൊരു ഫീച്ചറിലേക്ക് നിങ്ങൾക്ക് ഈ കുറുക്കുവഴി മാറ്റാനാവും."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ഓണാക്കുക"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ഓണാക്കരുത്"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index b95b6897eb7a..6b7a3653a74c 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Дууг санал болгосноос чанга болгож өсгөх үү?\n\nУрт хугацаанд чанга хөгжим сонсох нь таны сонсголыг муутгаж болно."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Хүртээмжийн товчлолыг ашиглах уу?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Товчлол асаалттай үед дууны түвшний хоёр товчлуурыг хамтад нь 3 секунд дарснаар хандалтын онцлогийг эхлүүлнэ."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Хандалтын онцлогуудыг асаах уу?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Дууны түвшний түлхүүрийг хэдэн секундийн турш зэрэг дарснаар хандалтын онцлогууд асна. Энэ нь таны төхөөрөмжийн ажиллах зарчмыг өөрчилж болзошгүй.\n\nОдоогийн онцлогууд:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТа сонгосон онцлогуудыг Тохиргоо &gt; Хандалт хэсэгт өөрчлөх боломжтой."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g>-г асаах уу?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Дууны түвшний түлхүүрийг хэдэн секундийн турш зэрэг дарах нь хандалтын онцлог болох <xliff:g id="SERVICE">%1$s</xliff:g>-г асаадаг. Энэ нь таны төхөөрөмжийн ажиллах зарчмыг өөрчилж болзошгүй.\n\nТа Тохиргоо &gt; Хандалт хэсэгт энэ товчлолыг өөр онцлогт оноож өөрчлөх боломжтой."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Асаах"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Бүү асаа"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 40c7dbd384c9..4f8925c07bcb 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"शिफारस केलेल्‍या पातळीच्या वर आवाज वाढवायचा?\n\nउच्च आवाजात दीर्घ काळ ऐकण्‍याने आपल्‍या श्रवणशक्तीची हानी होऊ शकते."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"प्रवेशयोग्यता शॉर्टकट वापरायचा?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"शॉर्टकट सुरू असताना, दोन्ही व्‍हॉल्‍यूम बटणे तीन सेकंदांसाठी दाबून ठेवल्याने अ‍ॅक्सेसिबिलिटी वैशिष्ट्य सुरू होईल."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"अ‍ॅक्सेसिबिलिटी वैशिष्ट्ये सुरू करायची आहेत का?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"दोन्ही व्हॉल्यूम की काही सेकंद धरून ठेवल्याने अ‍ॅक्सेसिबिलिटी वैशिष्ट्ये सुरू होतात. यामुळे तुमचे डिव्हाइस कसे काम करते हे पूर्णपणे बदलते.\n\nसध्याची वैशिष्ट्ये:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nतुम्ही हा शॉर्टकट सेटिंग्ज &gt; अ‍ॅक्सेसिबिलिटी मध्ये बदलू शकता."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> सुरू करायची आहे का?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"दोन्ही व्हॉल्यूम की काही सेकंद धरून ठेवल्याने <xliff:g id="SERVICE">%1$s</xliff:g>, एक अ‍ॅक्सेसिबिलिटी वैशिष्ट्य सुरू होते. यामुळे तुमचे डिव्हाइस कसे काम करते हे पूर्णपणे बदलते.\n\nतुम्ही हा शॉर्टकट सेटिंग्ज &gt; अ‍ॅक्सेसिबिलिटी मध्ये बदलू शकता."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"सुरू करा"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"सुरू करू नका"</string>
@@ -1829,8 +1831,7 @@
<item quantity="other">%d तासासाठी</item>
<item quantity="one">1 तासासाठी</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>पर्यंत"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> पर्यंत"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> पर्यंत (पुढील अलार्म)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"तुम्ही बंद करेपर्यंत"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index fb24a06dd3a5..161be37174f9 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Naikkan kelantangan melebihi paras yang disyokorkan?\n\nMendengar pada kelantangan yang tinggi untuk tempoh yang lama boleh merosakkan pendengaran anda."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gunakan Pintasan Kebolehaksesan?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Apabila pintasan dihidupkan, tindakan menekan kedua-dua butang kelantangan selama 3 saat akan memulakan ciri kebolehaksesan."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Hidupkan ciri kebolehaksesan?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Tindakan menahan kedua-dua kekunci kelantangan selama beberapa saat akan menghidupkan ciri kebolehaksesan. Hal ini mungkin mengubah cara peranti anda berfungsi.\n\nCiri semasa:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAnda boleh menukar ciri yang dipilih dalam Tetapan &gt; Kebolehaksesan."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Hidupkan <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Tindakan menahan kedua-dua kekunci kelantangan selama beberapa saat akan menghidupkan <xliff:g id="SERVICE">%1$s</xliff:g>, iaitu satu ciri kebolehaksesan. Ini mungkin mengubah cara peranti anda berfungsi.\n\nAnda boleh menukar pintasan ini kepada ciri lain dalam Tetapan &gt; Kebolehaksesan."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Hidupkan"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Jangan hidupkan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 2c3fbcbeac41..a21e4bc69227 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"အသံကို အကြံပြုထားသည့် ပမာဏထက် မြှင့်ပေးရမလား?\n\nအသံကို မြင့်သည့် အဆင့်မှာ ကြာရှည်စွာ နားထောင်ခြင်းက သင်၏ နားကို ထိခိုက်စေနိုင်သည်။"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"အများသုံးစွဲနိုင်မှု ဖြတ်လမ်းလင့်ခ်ကို အသုံးပြုလိုပါသလား။"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ဖြတ်လမ်းလင့်ခ်ကို ဖွင့်ထားစဉ် အသံထိန်းခလုတ် နှစ်ခုစလုံးကို ၃ စက္ကန့်ခန့် ဖိထားခြင်းဖြင့် အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုကို ဖွင့်နိုင်သည်။"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများ ဖွင့်မလား။"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"အသံခလုတ်နှစ်ခုလုံးကို စက္ကန့်အနည်းငယ် ဖိထားခြင်းက အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများ ဖွင့်ပေးသည်။ ဤလုပ်ဆောင်ချက်က သင့်စက်အလုပ်လုပ်ပုံကို ပြောင်းလဲနိုင်သည်။\n\nလက်ရှိ ဝန်ဆောင်မှုများ-\n<xliff:g id="SERVICE">%1$s</xliff:g>\n\'ဆက်တင်များ &gt; အများသုံးစွဲနိုင်မှု\' တွင် ရွေးထားသည့် ဝန်ဆောင်မှုများကို ပြောင်းနိုင်သည်။"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ဖွင့်မလား။"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"အသံခလုတ်နှစ်ခုလုံးကို စက္ကန့်အနည်းငယ် ဖိထားခြင်းက အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုဖြစ်သော <xliff:g id="SERVICE">%1$s</xliff:g> ကို ဖွင့်ပေးသည်။ ဤလုပ်ဆောင်ချက်က သင့်စက်အလုပ်လုပ်ပုံကို ပြောင်းလဲနိုင်သည်။\n\nဤဖြတ်လမ်းလင့်ခ်ကို \'ဆက်တင်များ &gt; အများသုံးစွဲနိုင်မှု\' တွင် နောက်ဝန်ဆောင်မှုတစ်ခုသို့ ပြောင်းနိုင်သည်။"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ဖွင့်ရန်"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"မဖွင့်ပါနှင့်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index fced2070c93b..d25c3ca555ec 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vil du øke volumet til over anbefalt nivå?\n\nHvis du hører på et høyt volum over lengre perioder, kan det skade hørselen din."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vil du bruke tilgjengelighetssnarveien?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Når snarveien er på, starter en tilgjengelighetsfunksjon når du trykker inn begge volumknappene i tre sekunder."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vil du slå på tilgjengelighetsfunksjoner?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hvis du holder inne volumtastene i noen sekunder, slås tilgjengelighetsfunksjoner på. Dette kan endre hvordan enheten din fungerer.\n\nNåværende funksjoner:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kan endre valgte funksjoner i Innstillinger &gt; Tilgjengelighet."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vil du slå på <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hvis du holder inne begge volumtastene i noen sekunder, slår du på <xliff:g id="SERVICE">%1$s</xliff:g>, en tilgjengelighetsfunksjon. Dette kan endre hvordan enheten din fungerer.\n\nDu kan endre denne snarveien til en annen funksjon i Innstillinger &gt; Tilgjengelighet."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Slå på"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ikke slå på"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index fadabe83dd55..2fc711f5a11b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"सिफारिस तहभन्दा आवाज ठुलो गर्नुहुन्छ?\n\nलामो समय सम्म उच्च आवाजमा सुन्दा तपाईँको सुन्ने शक्तिलाई हानी गर्न सक्छ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"पहुँच सम्बन्धी सर्टकट प्रयोग गर्ने हो?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"यो सर्टकट सक्रिय हुँदा, ३ सेकेन्डसम्म दुवै भोल्युम बटन थिच्नुले पहुँचसम्बन्धी कुनै सुविधा सुरु गर्ने छ।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"पहुँचसम्बन्धी सुविधाहरू सक्रिय गर्ने हो?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"केही सेकेन्डसम्म दुवै भोल्युम बटन थिचिराख्नुभयो भने पहुँचसम्बन्धी सुविधाहरू सक्रिय हुन्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nहालका सुविधाहरू:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nतपाईं सेटिङ &gt; पहुँचमा गएर चयन गरिएका सुविधाहरू परिवर्तन गर्न सक्नुहुन्छ।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> सक्रिय गर्ने हो?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"केही सेकेन्डसम्म दुवै भोल्युम बटन थिचिराख्नुले <xliff:g id="SERVICE">%1$s</xliff:g> नामक पहुँचसम्बन्धी सुविधा सक्रिय गर्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nतपाईं सेटिङ &gt; पहुँचमा गई यो सर्टकटमार्फत अर्को सुविधा खुल्ने बनाउन सक्नुहुन्छ।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"सक्रिय गरियोस्"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"सक्रिय नगरियोस्"</string>
@@ -1829,8 +1831,7 @@
<item quantity="other">%d घन्टाका लागि</item>
<item quantity="one">१ घन्टाको लागि</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> सम्म"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> सम्म"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (अर्को अलार्म) सम्म"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"तपाईंले निष्क्रिय नपार्नुभएसम्म"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 337bf17fd7fa..11a873ada063 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls je langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Snelkoppeling toegankelijkheid gebruiken?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Als de snelkoppeling is ingeschakeld, kun je drie seconden op beide volumeknoppen drukken om een toegankelijkheidsfunctie te starten."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Toegankelijkheidsfuncties inschakelen?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Als je beide volumetoetsen een paar seconden ingedrukt houdt, schakel je de toegankelijkheidsfuncties in. Hierdoor kan de manier veranderen waarop je apparaat werkt.\n\nHuidige functies:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nJe kunt de geselecteerde functies wijzigen via Instellingen &gt; Toegankelijkheid."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> inschakelen?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Als je beide volumetoetsen een paar seconden ingedrukt houdt, wordt de toegankelijkheidsfunctie <xliff:g id="SERVICE">%1$s</xliff:g> ingeschakeld. Hierdoor kan de manier veranderen waarop je apparaat werkt.\n\nJe kunt deze sneltoets op een andere functie instellen via Instellingen &gt; Toegankelijkheid."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Inschakelen"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Niet inschakelen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 85ca06564d7a..188a9bc13bd3 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ମାତ୍ରା ବଢ଼ାଇ ସୁପାରିଶ ସ୍ତର ବଢ଼ାଉଛନ୍ତି? \n\n ଲମ୍ବା ସମୟ ପର୍ଯ୍ୟନ୍ତ ଉଚ୍ଚ ଶବ୍ଦରେ ଶୁଣିଲେ ଆପଣଙ୍କ ଶ୍ରବଣ ଶକ୍ତି ଖରାପ ହୋଇପାରେ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ଆକ୍ସେସବିଲିଟି ଶର୍ଟକଟ୍‍ ବ୍ୟବହାର କରିବେ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ସର୍ଟକଟ୍ ଚାଲୁ ଥିବା ବେଳେ, ଉଭୟ ଭଲ୍ୟୁମ୍ ବଟନ୍ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଦ୍ୱାରା ଏକ ଆକ୍ସେସବିଲିଟି ଫିଚର୍ ଆରମ୍ଭ ହେବ।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ଆକ୍ସେସିବିଲିଟୀ ଫିଚରଗୁଡ଼ିକୁ ଚାଲୁ କରିବେ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"କିଛି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ୍ କୀ’କୁ ଧରି ରଖିବା ଫଳରେ ଆକ୍ସେସିବିଲିଟୀ ଫିଚରଗୁଡ଼ିକ ଚାଲୁ ହୁଏ। ଏହା ଆପଣଙ୍କ ଡିଭାଇସ୍ କିପରି କାମ କରେ ତାହା ପରିବର୍ତ୍ତନ କରିପାରେ।\n\nବର୍ତ୍ତମାନର ଫିଚରଗୁଡ଼ିକ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n ଆପଣ ସେଟିଂସ୍ &amp;gt ଆକ୍ସେସିବିଲିଟୀରେ ଚୟନିତ ଫିଚରଗୁଡ଼ିକୁ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ଚାଲୁ କରିବେ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"କିଛି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ୍ କୀ’କୁ ଧରି ରଖିବା ଫଳରେ ଏକ ଆକ୍ସେସିବିଲିଟୀ ଫିଚର୍ <xliff:g id="SERVICE">%1$s</xliff:g> ଚାଲୁ ହୁଏ। ଏହା ଆପଣଙ୍କ ଡିଭାଇସ୍ କିପରି କାମ କରେ ତାହା ପରିବର୍ତ୍ତନ କରିପାରେ।\n\nଆପଣ ସେଟିଂସ୍ &amp;gt ଆକ୍ସେସିବିଲିଟୀରେ ଏହି ସର୍ଚକଟକୁ ଅନ୍ୟ ଏକ ଫିଚରରେ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ଚାଲୁ କରନ୍ତୁ"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ଚାଲୁ କରନ୍ତୁ ନାହିଁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index ed663a8bb5d6..22d5f4410cbe 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ਕੀ ਵੌਲਿਊਮ ਸਿਫ਼ਾਰਸ਼ ਕੀਤੇ ਪੱਧਰ ਤੋਂ ਵਧਾਉਣੀ ਹੈ?\n\nਲੰਮੇ ਸਮੇਂ ਤੱਕ ਉੱਚ ਵੌਲਿਊਮ ਤੇ ਸੁਣਨ ਨਾਲ ਤੁਹਾਡੀ ਸੁਣਨ ਸ਼ਕਤੀ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚ ਸਕਦਾ ਹੈ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ਕੀ ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਵਰਤਣਾ ਹੈ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ਸ਼ਾਰਟਕੱਟ ਚਾਲੂ ਹੋਣ \'ਤੇ, ਕਿਸੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ ਬਟਨਾਂ ਨੂੰ 3 ਸਕਿੰਟ ਲਈ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ਕੀ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ਕੁਝ ਸਕਿੰਟਾਂ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਨੂੰ ਦਬਾਈ ਰੱਖਣਾ, ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕਰ ਦਿੰਦਾ ਹੈ। ਇਹ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਕੰਮ ਕਰਨ ਦੇ ਤਰੀਕੇ ਨੂੰ ਬਦਲ ਸਕਦਾ ਹੈ।\n\nਮੌਜੂਦਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nਸੈਟਿੰਗਾਂ ਅਤੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿੱਚ ਤੁਸੀਂ ਚੁਣੀਆਂ ਗਈਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਬਦਲ ਸਕਦੇ ਹੋ।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"ਕੀ <xliff:g id="SERVICE">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ਕੁਝ ਸਕਿੰਟਾਂ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਨੂੰ ਦਬਾਈ ਰੱਖਣਾ <xliff:g id="SERVICE">%1$s</xliff:g>, ਇੱਕ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਚਾਲੂ ਕਰ ਦਿੰਦਾ ਹੈ। ਇਹ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਕੰਮ ਕਰਨ ਦੇ ਤਰੀਕੇ ਨੂੰ ਬਦਲ ਸਕਦਾ ਹੈ।\n\nਸੈਟਿੰਗਾਂ ਅਤੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿੱਚ ਤੁਸੀਂ ਇਸ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਕਿਸੇ ਹੋਰ ਵਿਸ਼ੇਸ਼ਤਾ ਵਿੱਚ ਬਦਲ ਸਕਦੇ ਹੋ।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ਚਾਲੂ ਕਰੋ"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ਚਾਲੂ ਨਾ ਕਰੋ"</string>
@@ -1829,8 +1831,7 @@
<item quantity="one">%d ਘੰਟਿਆਂ ਲਈ</item>
<item quantity="other">%d ਘੰਟਿਆਂ ਲਈ</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ (ਅਗਲਾ ਅਲਾਰਮ)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ ਹੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 16b3fafe3520..c9c3b4677bc0 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zwiększyć głośność ponad zalecany poziom?\n\nSłuchanie głośno przez długi czas może uszkodzić Twój słuch."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Użyć skrótu do ułatwień dostępu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Gdy skrót jest włączony, jednoczesne naciskanie przez trzy sekundy obu przycisków głośności uruchamia funkcję ułatwień dostępu."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Włączyć ułatwienia dostępu?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Przytrzymanie obu klawiszy głośności przez kilka sekund włącza ułatwienia dostępu. Może to zmienić sposób działania urządzenia.\n\nBieżące funkcje:\n<xliff:g id="SERVICE">%1$s</xliff:g>\naby zmienić wybrane funkcje, kliknij Ustawienia &gt; Ułatwienia dostępu."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Włączyć usługę <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Przytrzymanie obu klawiszy głośności przez kilka sekund włącza usługę <xliff:g id="SERVICE">%1$s</xliff:g>, stanowiącą ułatwienie dostępu. Może to zmienić sposób działania urządzenia.\n\nAby zmienić ten skrót i wskazać inną funkcję, kliknij Ustawienia &gt; Ułatwienia dostępu."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Włącz"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nie włączaj"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 2883d6fd8ae2..221e29c84660 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usar atalho de Acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando o atalho estiver ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Ativar os recursos de acessibilidade?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Manter as duas teclas de volume pressionadas por alguns segundos ativa os recursos de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nRecursos atuais:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nÉ possível mudar os recursos selecionados em \"Config. &gt; Acessibilidade\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Ativar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Manter as duas teclas de volume pressionadas por alguns segundos ativa o serviço <xliff:g id="SERVICE">%1$s</xliff:g>, um recurso de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nÉ possível trocar o uso desse atalho para outro recurso em \"Config. &gt; Acessibilidade\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ativar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Não ativar"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 3bc9530ab46e..e908c7f5bb87 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir com um volume elevado durante longos períodos poderá ser prejudicial para a sua audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Pretende utilizar o atalho de acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando o atalho está ativado, premir ambos os botões de volume durante 3 segundos inicia uma funcionalidade de acessibilidade."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Pretende ativar as funcionalidades de acessibilidade?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Manter premidas ambas as teclas de volume durante alguns segundos ativa as funcionalidades de acessibilidade. Estas podem alterar a forma como o seu dispositivo funciona.\n\nFuncionalidades atuais:\n<xliff:g id="SERVICE">%1$s</xliff:g>\npode alterar as funcionalidades selecionadas em Definições &gt; Acessibilidade."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Pretende ativar o serviço <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Manter premidas ambas as teclas de volume durante alguns segundos ativa o serviço <xliff:g id="SERVICE">%1$s</xliff:g>, uma funcionalidade de acessibilidade. Esta pode alterar a forma como o seu dispositivo funciona.\n\nPode alterar este atalho para outra funcionalidade em Definições &gt; Acessibilidade."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ativar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Não ativar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 2883d6fd8ae2..221e29c84660 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usar atalho de Acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando o atalho estiver ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Ativar os recursos de acessibilidade?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Manter as duas teclas de volume pressionadas por alguns segundos ativa os recursos de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nRecursos atuais:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nÉ possível mudar os recursos selecionados em \"Config. &gt; Acessibilidade\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Ativar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Manter as duas teclas de volume pressionadas por alguns segundos ativa o serviço <xliff:g id="SERVICE">%1$s</xliff:g>, um recurso de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nÉ possível trocar o uso desse atalho para outro recurso em \"Config. &gt; Acessibilidade\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ativar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Não ativar"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index d5a9297c8298..c2dea7eb0058 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ridicați volumul mai sus de nivelul recomandat?\n\nAscultarea la volum ridicat pe perioade lungi de timp vă poate afecta auzul."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utilizați comanda rapidă pentru accesibilitate?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Atunci când comanda rapidă este activată, dacă apăsați ambele butoane de volum timp de trei secunde, veți lansa o funcție de accesibilitate."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Activați funcțiile de accesibilitate?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Dacă apăsați ambele taste de volum câteva secunde, activați funcțiile de accesibilitate. Acest lucru poate schimba funcționarea dispozitivului.\n\nFuncțiile actuale:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuteți schimba funcțiile selectate din Setări &gt; Accesibilitate."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Activați <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Dacă apăsați ambele taste de volum câteva secunde, activați funcția de accesibilitate <xliff:g id="SERVICE">%1$s</xliff:g>. Acest lucru poate schimba funcționarea dispozitivului.\n\nPuteți alege altă funcție pentru această comandă în Setări &gt; Accesibilitate."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activați"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nu activați"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 3339fb52b72a..f8d6dfb2757e 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Установить громкость выше рекомендуемого уровня?\n\nВоздействие громкого звука в течение долгого времени может привести к повреждению слуха."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Использовать быстрое включение?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Чтобы использовать функцию специальных возможностей, когда она включена, нажмите и удерживайте обе кнопки регулировки громкости в течение трех секунд."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Включить специальные возможности?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Чтобы включить специальные возможности, нажмите обе кнопки регулировки громкости и удерживайте несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nТекущие функции:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nЧтобы изменить выбранные функции, перейдите в настройки и нажмите \"Специальные возможности\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Включить функцию \"<xliff:g id="SERVICE">%1$s</xliff:g>\"?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Чтобы включить функцию \"<xliff:g id="SERVICE">%1$s</xliff:g>\", нажмите обе кнопки регулировки громкости на несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nЧтобы назначить это сочетание клавиш другой функции, перейдите в настройки и выберите \"Специальные возможности\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Включить"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не включать"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index c26f23f3cc6d..63615f70bc7a 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"නිර්දේශිතයි මට්ටමට වඩා ශබ්දය වැඩිද?\n\nදිගු කාලයක් සඳහා ඉහළ ශබ්දයක් ඇසීමෙන් ඇතැම් විට ඔබගේ ඇසීමට හානි විය හැක."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ප්‍රවේශ්‍යතා කෙටිමඟ භාවිතා කරන්නද?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"කෙටිමග ක්‍රියාත්මක විට, හඬ පරිමා බොත්තම් දෙකම තත්පර 3ක් තිස්සේ එබීමෙන් ප්‍රවේශ්‍යතා විශේෂාංගය ආරම්භ වනු ඇත."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ප්‍රවේශ්‍යතා විශේෂාංග ක්‍රියාත්මක කරන්නද?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"හඬ පරිමා යතුරු දෙකම තත්පර කීපයකට පහළට අල්ලාගෙන සිටීම ප්‍රවේශ්‍යතා විශේෂාංග ක්‍රියාත්මක කරයි. මෙය ඔබේ උපාංගය ක්‍රියා කරන ආකාරය වෙනස් කළ හැකිය.\n\nවත්මන් විශේෂාංග:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nඔබට තේරූ විශේෂාංග සැකසීම් &gt; ප්‍රවේශ්‍යතාව හි වෙනස් කළ හැකිය."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ක්‍රියාත්මක කරන්නද?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"හඬ පරිමා යතුරු දෙකම තත්පර කීපයකට පහළට අල්ලාගෙන සිටීම ප්‍රවේශ්‍යතා විශේෂාංගයක් වන <xliff:g id="SERVICE">%1$s</xliff:g> ක්‍රියාත්මක කරයි. මෙය ඔබේ උපාංගය ක්‍රියා කරන ආකාරය වෙනස් කළ හැකිය.\n\nඔබට මෙම කෙටිමග සැකසීම් &gt; ප්‍රවේශ්‍යතාව හි තවත් විශේෂාංගයකට වෙනස් කළ හැකිය."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ක්‍රියාත්මක කරන්න"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ක්‍රියාත්මක නොකරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 1a4f07e397e6..4f0704b39a45 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zvýšiť hlasitosť nad odporúčanú úroveň?\n\nDlhodobé počúvanie pri vysokej hlasitosti môže poškodiť váš sluch."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Použiť skratku dostupnosti?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Keď je skratka zapnutá, stlačením obidvoch tlačidiel hlasitosti na tri sekundy spustíte funkciu dostupnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Chcete zapnúť funkcie dostupnosti?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Pridržaním oboch tlačidiel hlasitosti na niekoľko sekúnd zapnete funkcie dostupnosti. Môže sa tým zmeniť spôsob fungovania vášho zariadenia.\n\nAktuálne funkcie:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nVybrané funkcie môžete zmeniť v časti Nastavenia &gt; Dostupnosť."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Chcete zapnúť <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Pridržaním oboch klávesov hlasitosti na niekoľko sekúnd zapnete funkciu dostupnosti <xliff:g id="SERVICE">%1$s</xliff:g>. Môže sa tým zmeniť spôsob fungovania vášho zariadenia.\n\nTúto skratku môžete zmeniť na inú funkciu v časti Nastavenia &gt; Dostupnosť."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Zapnúť"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nezapínať"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 0079e389116f..85f11241d09b 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ali želite povečati glasnost nad priporočeno raven?\n\nDolgotrajno poslušanje pri veliki glasnosti lahko poškoduje sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite uporabljati bližnjico funkcij za ljudi s posebnimi potrebami?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Ko je bližnjica vklopljena, pritisnite gumba za glasnost in ju pridržite tri sekunde, če želite zagnati funkcijo za ljudi s posebnimi potrebami."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Želite vklopiti funkcije za ljudi s posebnimi potrebami?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Če za nekaj sekund pridržite obe tipki za glasnost, boste vklopili funkcije za ljudi s posebnimi potrebami. To lahko spremeni način delovanja naprave.\n\nTrenutne funkcije:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nIzbrane funkcije lahko spremenite v meniju »Nastavitve« &gt; »Funkcije za ljudi s posebnimi potrebami«."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Želite vklopiti storitev <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Če za nekaj sekund pridržite obe tipki za glasnost, boste vklopili storitev <xliff:g id="SERVICE">%1$s</xliff:g>, ki je funkcija za ljudi s posebnimi potrebami. To lahko spremeni način delovanja naprave.\n\nTo bližnjico lahko v meniju »Nastavitve« &gt; »Funkcije za ljudi s posebnimi potrebami« spremenite, da bo uporabljena za drugo funkcijo."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Vklopi"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne vklopi"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index b29582c05d6b..4fd25bd7276f 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Të ngrihet volumi mbi nivelin e rekomanduar?\n\nDëgjimi me volum të lartë për periudha të gjata mund të dëmtojë dëgjimin."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Të përdoret shkurtorja e qasshmërisë?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kur shkurtorja është e aktivizuar, shtypja e të dy butonave për 3 sekonda do të nisë një funksion qasshmërie."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Të aktivizohen veçoritë e qasshmërisë?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Mbajtja shtypur e dy tasteve të volumit për pak sekonda aktivizon veçoritë e qasshmërisë. Kjo mund të ndryshojë mënyrën se si funksionon pajisja jote.\n\nVeçoritë aktuale:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nKe ndryshuar veçoritë e zgjedhura te Cilësimet &gt; Qasshmëria."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Të aktivizohet <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Mbajtja shtypur e dy tasteve të volumit për pak sekonda aktivizon <xliff:g id="SERVICE">%1$s</xliff:g>, një veçori të qasshmërisë. Kjo mund të ndryshojë mënyrën se si funksionon pajisja jote.\n\nMund të ndryshosh këtë shkurtore te një veçori tjetër te Cilësimet &gt; Qasshmëria."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivizo"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Mos e aktivizo"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f24e285700c5..aa37ea0b64de 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Желите да појачате звук изнад препорученог нивоа?\n\nСлушање гласне музике дуже време може да вам оштети слух."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Желите ли да користите пречицу за приступачност?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Када је пречица укључена, притисните оба дугмета за јачину звука да бисте покренули функцију приступачности."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Желите ли да укључите функције приступачности?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ако задржите оба тастера за јачину звука пар секунди, укључиће се функције приступачности. То може да промени начин рада уређаја.\n\nПостојеће функције:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nМожете да промените изабране функције у одељку Подешавања &gt; Приступачност."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Желите ли да укључите услугу <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ако задржите оба тастера за јачину звука пар секунди, укључује се <xliff:g id="SERVICE">%1$s</xliff:g>, функција приступачности. То може да промени начин рада уређаја.\n\nМожете да промените функцију на коју се односи ова пречица у одељку Подешавања &gt; Приступачност."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Укључи"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не укључуј"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 5c9ce2c8a608..acea0d532e11 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vill du höja volymen över den rekommenderade nivån?\n\nAtt lyssna med stark volym långa stunder åt gången kan skada hörseln."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vill du använda Aktivera tillgänglighet snabbt?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"När kortkommandot har aktiverats startar du en tillgänglighetsfunktion genom att trycka ned båda volymknapparna i tre sekunder."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vill du aktivera tillgänglighetsfunktionerna?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Om du trycker ned båda volymknapparna i ett par sekunder aktiveras tillgänglighetsfunktionerna. Det kan få enheten ett fungera annorlunda.\n\nAktuella funktioner:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kan ändra vilka funktioner som aktiveras under Inställningar &gt; Tillgänglighet."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vill du aktivera <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Om du trycker ned båda volymknapparna i ett par sekunder aktiveras <xliff:g id="SERVICE">%1$s</xliff:g>, en tillgänglighetsfunktion. Det kan leda till att enheten fungerar annorlunda.\n\nDu kan ändra vilken funktion som ska aktiveras med genvägen under Inställningar &gt; Tillgänglighet."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivera"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Aktivera inte"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 55db3eb45225..aa0c520c53aa 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ungependa kupandisha sauti zaidi ya kiwango kinachopendekezwa?\n\nKusikiliza kwa sauti ya juu kwa muda mrefu kunaweza kuharibu uwezo wako wa kusikia."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ungependa kutumia njia ya mkato ya ufikivu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Unapowasha kipengele cha njia ya mkato, hatua ya kubonyeza vitufe vyote viwili vya sauti kwa sekunde tatu itafungua kipengele cha ufikivu."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Ungependa kuwasha vipengele vya ufikivu?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hatua ya kushikilia chini vitufe vyote viwili vya sauti kwa sekunde chache huwasha vipengele vya ufikivu. Huenda hatua hii ikabadilisha jinsi kifaa chako kinavyofanya kazi.\n\nVipengele vya sasa:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nUnaweza kubadilisha vipengele ulivyochagua katika Mipangilio &gt; Ufikivu."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Ungependa kuwasha <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hatua ya kushikilia chini vitufe vyote viwili vya sauti kwa sekunde chache huwasha <xliff:g id="SERVICE">%1$s</xliff:g>, kipengele cha ufikivu. Huenda hatua hii ikabadilisha jinsi kifaa chako kinavyofanya kazi.\n\nUnaweza kubadilisha njia hii ya mkato iwe kipengele kingine katika Mipangilio &gt; Ufikivu."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Washa"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Usiwashe"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index be030d16b1ed..068c3752ed35 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"பரிந்துரைத்த அளவை விட ஒலியை அதிகரிக்கவா?\n\nநீண்ட நேரத்திற்கு அதிகளவில் ஒலி கேட்பது கேட்கும் திறனைப் பாதிக்கலாம்."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"அணுகல்தன்மை ஷார்ட்கட்டைப் பயன்படுத்தவா?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ஷார்ட்கட் இயக்கத்தில் இருக்கும்போது ஒலியளவு பட்டன்கள் இரண்டையும் 3 வினாடிகளுக்கு அழுத்தினால் அணுகல்தன்மை அம்சம் இயக்கப்படும்."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"அணுகல்தன்மை அம்சங்களை ஆன் செய்யவா?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"இரண்டு ஒலியளவு விசைகளையும் சில விநாடிகள் பிடித்திருந்தால் அணுகல்தன்மை அம்சங்கள் ஆன் செய்யப்படும். இதனால் உங்கள் சாதனம் வேலை செய்யும் முறை மாறக்கூடும்.\n\nதற்போதைய அம்சங்கள்:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nதேர்ந்தெடுத்த அம்சங்களை அமைப்புகள் &gt; அணுகல்தன்மைக்குச் சென்று உங்களால் மாற்ற முடியும்."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ஐ ஆன் செய்யவா?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"இரண்டு ஒலியளவு விசைகளையும் சில விநாடிகள் பிடித்திருப்பதால் அணுகல்தன்மை அம்சமான <xliff:g id="SERVICE">%1$s</xliff:g> ஆன் ஆகும். இதனால் உங்கள் சாதனம் வேலை செய்யும் முறை மாறக்கூடும்.\n\nஅமைப்புகள் &gt; அணுகல்தன்மைக்குச் சென்று இந்த ஷார்ட்கட்டை வேறு அம்சத்திற்கு மாற்ற முடியும்."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ஆன் செய்"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ஆன் செய்யாதே"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 0966d235f5dd..601608265a21 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"వాల్యూమ్‌ను సిఫార్సు చేయబడిన స్థాయి కంటే ఎక్కువగా పెంచాలా?\n\nసుదీర్ఘ వ్యవధుల పాటు అధిక వాల్యూమ్‌లో వినడం వలన మీ వినికిడి శక్తి దెబ్బ తినవచ్చు."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"యాక్సెస్ సామర్థ్యం షార్ట్‌కట్‌ను ఉపయోగించాలా?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"షార్ట్‌కట్ ఆన్ చేసి ఉన్నప్పుడు, రెండు వాల్యూమ్ బటన్‌లను 3 సెకన్ల పాటు నొక్కి ఉంచితే యాక్సెస్ సౌలభ్య ఫీచర్ ప్రారంభం అవుతుంది."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"యాక్సెసిబిలిటీ‌ ఫీచ‌ర్‌ల‌ను ఆన్ చేయాలా?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"రెండు వాల్యూమ్ కీలను కొంత సేపు నొక్కి పట్టుకుంటే యాక్సెసిబిలిటీ ఫీచ‌ర్‌లు ఆన్ అవుతాయి. ఇది మీ పరికరం పని చేసే విధానాన్ని మార్చవచ్చు.\n\nప్రస్తుత ఫీచర్లు:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nఎంపిక చేసిన ఫీచర్లను మీరు సెట్టింగ్‌లు&gt;యాక్సెసిబిలిటీలో మార్చవచ్చు."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ఆన్ చేయాాలా?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"రెండు వాల్యూమ్ కీలను కొన్ని సెకన్ల పాటు నొక్కి పట్టుకోవడం ద్వారా యాక్సెసిబిలిటీ అయిన <xliff:g id="SERVICE">%1$s</xliff:g> ఆన్ అవుతుంది. ఇది మీ పరికరం పని చేసే విధానాన్ని మార్చవచ్చు.\n\nసెట్టింగ్‌లు &gt; యాక్సెసిబిలిటీలో, వేరొక ఫీచర్‌ను ప్రారంభించేలా ఈ షార్ట్ కట్‌ను మీరు మార్చవచ్చు."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ఆన్ చేయి"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ఆన్ చేయకండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 7ff76ec7bde6..2feea145f714 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"นี่เป็นการเพิ่มระดับเสียงเกินระดับที่แนะนำ\n\nการฟังเสียงดังเป็นเวลานานอาจทำให้การได้ยินของคุณบกพร่องได้"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ใช้ทางลัดการช่วยเหลือพิเศษไหม"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"เมื่อทางลัดเปิดอยู่ การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มนาน 3 วินาทีจะเริ่มฟีเจอร์การช่วยเหลือพิเศษ"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ต้องการเปิดฟีเจอร์การช่วยเหลือพิเศษใช่ไหม"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 2-3 วินาทีจะเปิดฟีเจอร์การช่วยเหลือพิเศษ การดำเนินการนี้อาจเปลี่ยนแปลงลักษณะการทำงานของอุปกรณ์\n\nฟีเจอร์ปัจจุบัน:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nคุณจะเปลี่ยนฟีเจอร์ที่เลือกไว้ได้ในการตั้งค่า &gt; การช่วยเหลือพิเศษ"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"ต้องการเปิด <xliff:g id="SERVICE">%1$s</xliff:g> ใช่ไหม"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 2-3 วินาทีจะเปิด <xliff:g id="SERVICE">%1$s</xliff:g> ซึ่งเป็นฟีเจอร์การช่วยเหลือพิเศษ การดำเนินการนี้อาจเปลี่ยนแปลงลักษณะการทำงานของอุปกรณ์\n\nคุณแก้ไขทางลัดนี้ให้เปิดฟีเจอร์อื่นได้ในการตั้งค่า &gt; การช่วยเหลือพิเศษ"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"เปิด"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ไม่ต้องเปิด"</string>
@@ -2031,7 +2033,7 @@
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> และอีก <xliff:g id="COUNT_3">%d</xliff:g> ไฟล์</item>
<item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> และอีก <xliff:g id="COUNT_1">%d</xliff:g> ไฟล์</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ไม่มีบุคคลที่แนะนำให้แชร์ด้วย"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ไม่พบใครที่แนะนำให้แชร์ด้วย"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"รายชื่อแอป"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"แอปนี้ไม่ได้รับอนุญาตให้บันทึกเสียงแต่จะบันทึกเสียงผ่านอุปกรณ์ USB นี้ได้"</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"หน้าแรก"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 7d9c7173c84e..41ea05717d60 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Lakasan ang volume nang lagpas sa inirerekomendang antas?\n\nMaaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gagamitin ang Shortcut sa Accessibility?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kapag naka-on ang shortcut, magsisimula ang isang feature ng pagiging naa-access kapag pinindot ang parehong button ng volume."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"I-on ang mga feature ng accessibility?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Mao-on ang mga feature ng accessibility kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nMga kasalukuyang feature:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuwede mong baguhin ang mga napiling feature sa Mga Setting &gt; Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"I-on ang <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Mao-on ang feature ng accessibility na <xliff:g id="SERVICE">%1$s</xliff:g> kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nPuwede mong palitan ng ibang feature ang shortcut na ito sa Mga Setting &gt; Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"I-on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Huwag i-on"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a658bf18ab95..4513eb6bb1fd 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ses seviyesi önerilen düzeyin üzerine yükseltilsin mi?\n\nUzun süre yüksek ses seviyesinde dinlemek işitme duyunuza zarar verebilir."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Erişilebilirlik Kısayolu Kullanılsın mı?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kısayol açıkken ses düğmelerinin ikisini birden 3 saniyeliğine basılı tutmanız bir erişilebilirlik özelliğini başlatır."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Erişilebilirlik özellikleri etkinleştirilsin mi?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ses tuşlarının ikisini birden birkaç saniyeliğine basılı tutmak, erişilebilirlik özelliklerini açar. Bu, cihazınızın çalışma şeklini değiştirebilir.\n\nGeçerli özellikler:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nSeçilen özellikleri Ayarlar &gt; Erişilebilirlik\'te değiştirebilirsiniz."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> etkinleştirilsin mi?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ses tuşlarının ikisini birden birkaç saniyeliğine basılı tutmak <xliff:g id="SERVICE">%1$s</xliff:g> erişilebilirlik özelliğini etkinleştirir. Bu, cihazınızın çalışma şeklini değiştirebilir.\n\nBu kısayolu, Ayarlar &gt; Erişilebilirlik\'te başka bir özellikle değiştirebilirsiniz."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Etkinleştir"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Etkinleştirme"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 7e81784d1043..0f5ce7ecdee9 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Збільшити гучність понад рекомендований рівень?\n\nЯкщо слухати надто гучну музику тривалий час, можна пошкодити слух."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Використовувати швидке ввімкнення?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Якщо цей засіб увімкнено, ви можете активувати спеціальні можливості, утримуючи обидві кнопки гучності протягом трьох секунд."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Увімкнути спеціальні можливості?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Якщо втримувати обидві клавіші гучності впродовж кількох секунд, вмикаються спеціальні можливості. Це впливає на роботу пристрою.\n\nПоточні функції:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nВибрані функції можна змінити в налаштуваннях у меню спеціальних можливостей."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Увімкнути <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Якщо втримувати обидві клавіші гучності впродовж кількох секунд, буде ввімкнено спеціальні можливості – <xliff:g id="SERVICE">%1$s</xliff:g>. Це може вплинути на роботу пристрою.\n\nДля цієї комбінації клавіш можна вибрати іншу функцію в меню \"Налаштування &gt; Спеціальні можливості\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Увімкнути"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не вмикати"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index b9b9c586d8bb..77204800722e 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"والیوم کو تجویز کردہ سطح سے زیادہ کریں؟\n\nزیادہ وقت تک اونچی آواز میں سننے سے آپ کی سماعت کو نقصان پہنچ سکتا ہے۔"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ایکسیسبیلٹی شارٹ کٹ استعمال کریں؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"شارٹ کٹ آن ہونے پر، 3 سیکنڈ تک دونوں والیوم بٹنز کو دبانے سے ایک ایکسیسبیلٹی خصوصیت شروع ہو جائے گی۔"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ایکسیسبیلٹی خصوصیات آن کریں؟َ"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"دونوں والیوم کی کلیدوں کو کچھ سیکنڈز تک دبائیں رکھنے سے ایکسیسبیلٹی خصوصیات آن ہو جاتی ہیں۔ اس سے آپ کے آلے کے کام کرنے کا طریقہ تبدیل ہو سکتا ہے۔\n\nموجودہ خصوصیات:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nآپ ترتیبات اور ایکسیسبیلٹی میں منتخب کردہ خصوصیات کو تبدیل کر سکتے ہیں۔"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> آن کریں؟"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"والیوم کی دونوں کلیدوں کو کچھ سیکنڈز تک دبائے رکھنے سے <xliff:g id="SERVICE">%1$s</xliff:g> ایکسیسبیلٹی خصوصیت آن ہو جاتی ہے۔ اس سے آپ کے آلے کے کام کرنے کا طریقہ تبدیل ہو سکتا ہے۔\n\nآپ ترتیبات اور ایکسیسبیلٹی میں دیگر خصوصیت کے لیے اس شارٹ کٹ کو تبدیل کر سکتے ہیں۔"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"آن کریں"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"آن نہ کریں"</string>
@@ -1829,8 +1831,7 @@
<item quantity="other">‏‎%d گھنٹے کیلئے</item>
<item quantity="one">1 گھنٹہ کیلئے</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک (اگلا الارم)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"یہاں تک کہ آپ آف کر دیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 5edd9dc8332b..49caf1b34af0 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Tovush balandligi tavsiya etilgan darajadan ham yuqori qilinsinmi?\n\nUzoq vaqt davomida baland ovozda tinglash eshitish qobiliyatingizga salbiy ta’sir ko‘rsatishi mumkin."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Tezkor ishga tushirishdan foydalanilsinmi?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Maxsus imkoniyatlar funksiyasidan foydalanish uchun u yoniqligida ikkala tovush tugmasini 3 soniya bosib turing."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Maxsus imkoniyatlar yoqilsinmi?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Maxsus imkoniyatlarni yoqish uchun ikkala tovush tugmalarini bir necha soniya bosib turing. Qurilmangiz ishlashida oʻzgarish yuz berishi mumkin.\n\nJoriy funksiyalar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nTanlangan funksiyalarni Sozlamalar ichidagi Maxsus imkoniyatlar ustiga bosib oʻzgartirishingiz mumkin."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> yoqilsinmi?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"<xliff:g id="SERVICE">%1$s</xliff:g> funksiyasini yoqish uchun ikkala tovush tugmalarini bir necha soniya bosib turing. Qurilmangiz ishlashida oʻzgarish yuz berishi mumkin.\n\nBu tezkor tugmalarni boshqa funksiyaga Sozlamalar ichidagi Maxsus imkoniyatlar orqali tayinlash mumkin."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Yoqilsin"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Yoqilmasin"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index e3c765872cca..3b65cc1e6e22 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Bạn tăng âm lượng lên quá mức khuyên dùng?\n\nViệc nghe ở mức âm lượng cao trong thời gian dài có thể gây tổn thương thính giác của bạn."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Sử dụng phím tắt Hỗ trợ tiếp cận?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Khi phím tắt này đang bật, thao tác nhấn cả hai nút âm lượng trong 3 giây sẽ mở tính năng hỗ trợ tiếp cận."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Bật các tính năng hỗ trợ tiếp cận?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Thao tác nhấn và giữ cả hai phím âm lượng trong vài giây sẽ bật các tính năng hỗ trợ tiếp cận. Việc bật các tính năng này có thể thay đổi cách thiết bị của bạn hoạt động.\n\nCác tính năng hiện tại:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nBạn có thể thay đổi những tính năng đã chọn trong phần Cài đặt &gt; Hỗ trợ tiếp cận."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Bật <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Thao tác nhấn và giữ cả hai phím âm lượng trong vài giây sẽ bật <xliff:g id="SERVICE">%1$s</xliff:g>, một tính năng hỗ trợ tiếp cận. Việc bật tính năng này có thể thay đổi cách thiết bị của bạn hoạt động.\n\nBạn có thể chuyển phím tắt này thành một tính năng khác trong phần Cài đặt &gt; Hỗ trợ tiếp cận."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Bật"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Không bật"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index dee9d95b3a93..10472cb39bc6 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要将音量调高到建议的音量以上吗?\n\n长时间保持高音量可能会损伤听力。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"要使用无障碍快捷方式吗?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"启用这项快捷方式后,同时按下两个音量按钮 3 秒钟即可启动无障碍功能。"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"要开启无障碍功能吗?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"同时按住两个音量键几秒钟,即可开启无障碍功能。这样做可能会改变您设备的工作方式。\n\n当前功能:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n您可以在“设置”&gt;“无障碍”中更改所选功能。"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"要开启<xliff:g id="SERVICE">%1$s</xliff:g>吗?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"同时按住两个音量键几秒钟,即可开启<xliff:g id="SERVICE">%1$s</xliff:g>无障碍功能。这样做可能会改变您设备的工作方式。\n\n您可以在“设置”&gt;“无障碍”中将此快捷方式更改为开启另一项功能。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"开启"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"不开启"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 44ad8634223a..455ae54b4ba2 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要調高音量 (比建議的音量更大聲) 嗎?\n\n長時間聆聽高分貝音量可能會導致您的聽力受損。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"要使用無障礙功能快速鍵嗎?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"啟用快速鍵後,同時按住音量按鈕 3 秒便可啟用無障礙功能。"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"要開啟無障礙功能嗎?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"同時按下兩個音量鍵幾秒,以開啟無障礙功能。這可能會變更裝置的運作。\n\n目前功能:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n您可在「設定」&gt;「無障礙功能」中變更所選功能。"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"要啟用 <xliff:g id="SERVICE">%1$s</xliff:g> 嗎?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"同時按下兩個音量鍵幾秒,以開啟 <xliff:g id="SERVICE">%1$s</xliff:g> 無障礙功能。這可能會變更裝置的運作。\n\n您可在「設定」&gt;「無障礙功能」中變更此快速鍵。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"開啟"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"不要開啟"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 8831bf0b0815..5971d97d4bc4 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要調高音量,比建議的音量更大聲嗎?\n\n長時間聆聽高分貝音量可能會使你的聽力受損。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"要使用無障礙捷徑嗎?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"啟用捷徑功能,只要同時按下兩個音量按鈕 3 秒,就能啟動無障礙功能。"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"要開啟無障礙功能嗎?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"同時按住音量調高鍵和調低鍵數秒,即可開啟無障礙功能。這麼做可能會改變裝置的運作方式。\n\n目前的功能:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n你可以在 [設定] &gt; [無障礙設定] 中變更選取的功能。"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"要開啟「<xliff:g id="SERVICE">%1$s</xliff:g>」嗎?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"同時按住音量調高鍵和調低鍵數秒,即可開啟「<xliff:g id="SERVICE">%1$s</xliff:g>」無障礙功能。這麼做可能會改變裝置的運作方式。\n\n你可以在 [設定] &gt; [無障礙設定] 中變更這個快速鍵觸發的功能。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"開啟"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"不要開啟"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 5fb9fa61bb36..4a1033c5ab56 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Khuphukisa ivolumu ngaphezu kweleveli enconyiwe?\n\nUkulalela ngevolumu ephezulu izikhathi ezide kungahle kulimaze ukuzwa kwakho."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Sebenzisa isinqamuleli sokufinyelela?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Uma isinqamuleli sivuliwe, ukucindezela zombili izinkinobho zevolumu amasekhondi angu-3 kuzoqalisa isici sokufinyelela."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Uvula izici zokufinyelela?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ukubambela phansi bobabili okhiye bevolumu amasekhondi ambalwa kuvula izici zokufinyelela. Lokhu kungashintsha indlela idivayisi yakho esebenza ngayo.\n\nIzici zamanje:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nUngashintsha izici ezikhethiwe Kuzilungiselelo &gt; Ukufinyeleleka."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vula i-<xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ukubambela phansi bobabili okhiye bevolumu amasekhondi ambalwa kuvula i-<xliff:g id="SERVICE">%1$s</xliff:g>, eyisici sokufinyelela Lokhu kungashintsha indlela idivayisi yakho esebenza ngayo.\n\nUngashintshela lesi sinqamuleli kwesinye isici Kuzilungiselelo &gt; Ukufinyeleleka."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Vula"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ungavuli"</string>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 71cb2acde94e..56f18d5dc1d7 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1599,6 +1599,7 @@
</activity>
<activity android:name="android.app.activity.ActivityThreadTest$TestActivity"
+ android:configChanges="screenLayout|screenSize|orientation|smallestScreenSize"
android:supportsPictureInPicture="true"
android:exported="true">
</activity>
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index c9510918e555..7fa16130f25c 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.NotificationHistory.HistoricalNotification;
import android.graphics.drawable.Icon;
import android.os.Parcel;
-import android.util.Slog;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -31,6 +30,7 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class NotificationHistoryTest {
@@ -297,7 +297,7 @@ public class NotificationHistoryTest {
for (int i = 1; i <= 10; i++) {
HistoricalNotification n = getHistoricalNotification("pkg", i);
- if (i != 2) {
+ if (i != 2 && i != 4) {
postRemoveExpectedStrings.add(n.getPackage());
postRemoveExpectedStrings.add(n.getChannelName());
postRemoveExpectedStrings.add(n.getChannelId());
@@ -318,10 +318,10 @@ public class NotificationHistoryTest {
// 1 package name and 20 unique channel names and ids and 5 conversation ids
assertThat(history.getPooledStringsToWrite().length).isEqualTo(26);
- history.removeConversationFromWrite("pkg", "convo2");
+ history.removeConversationsFromWrite("pkg", Set.of("convo2", "convo4"));
- // 1 package names and 9 * 2 unique channel names and ids and 4 conversation ids
- assertThat(history.getPooledStringsToWrite().length).isEqualTo(23);
+ // 1 package names and 8 * 2 unique channel names and ids and 3 conversation ids
+ assertThat(history.getPooledStringsToWrite().length).isEqualTo(20);
assertThat(history.getNotificationsToWrite())
.containsExactlyElementsIn(postRemoveExpectedEntries);
}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 23534e2ebebc..8b25afba228e 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -69,6 +69,7 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Test for verifying {@link android.app.ActivityThread} class.
@@ -78,6 +79,7 @@ import java.util.concurrent.CountDownLatch;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ActivityThreadTest {
+ private static final int TIMEOUT_SEC = 10;
// The first sequence number to try with. Use a large number to avoid conflicts with the first a
// few sequence numbers the framework used to launch the test activity.
@@ -309,7 +311,7 @@ public class ActivityThreadTest {
transaction.addCallback(ActivityConfigurationChangeItem.obtain(activityConfigPortrait));
appThread.scheduleTransaction(transaction);
- activity.mTestLatch.await();
+ activity.mTestLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS);
activity.mConfigLatch.countDown();
activity.mConfigLatch = null;
@@ -352,7 +354,7 @@ public class ActivityThreadTest {
// Wait until the main thread is performing the configuration change for the configuration
// with sequence number BASE_SEQ + 1 before proceeding. This is to mimic the situation where
// the activity takes very long time to process configuration changes.
- activity.mTestLatch.await();
+ activity.mTestLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS);
config = new Configuration();
config.seq = BASE_SEQ + 2;
@@ -738,7 +740,7 @@ public class ActivityThreadTest {
mTestLatch.countDown();
}
try {
- mConfigLatch.await();
+ mConfigLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
diff --git a/core/tests/coretests/src/android/view/InsetsFlagsTest.java b/core/tests/coretests/src/android/view/InsetsFlagsTest.java
deleted file mode 100644
index b4302e79ed6a..000000000000
--- a/core/tests/coretests/src/android/view/InsetsFlagsTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-
-import static android.view.InsetsFlags.getAppearance;
-import static android.view.View.NAVIGATION_BAR_TRANSLUCENT;
-import static android.view.View.NAVIGATION_BAR_TRANSPARENT;
-import static android.view.View.STATUS_BAR_TRANSLUCENT;
-import static android.view.View.STATUS_BAR_TRANSPARENT;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
-import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
-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;
-import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
-
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.annotations.Presubmit;
-import android.view.WindowInsetsController.Appearance;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for {@link InsetsFlags}.
- *
- * <p>Build/Install/Run:
- * atest FrameworksCoreTests:InsetsFlagsTest
- *
- * <p>This test class is a part of Window Manager Service tests and specified in
- * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
- */
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class InsetsFlagsTest {
-
- @Test
- public void testGetAppearance() {
- assertContainsAppearance(APPEARANCE_LOW_PROFILE_BARS, SYSTEM_UI_FLAG_LOW_PROFILE);
- assertContainsAppearance(APPEARANCE_LIGHT_STATUS_BARS, SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
- assertContainsAppearance(APPEARANCE_LIGHT_NAVIGATION_BARS,
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
- assertContainsAppearance(APPEARANCE_OPAQUE_STATUS_BARS,
- 0xffffffff & ~(STATUS_BAR_TRANSLUCENT | STATUS_BAR_TRANSPARENT));
- assertContainsAppearance(APPEARANCE_OPAQUE_NAVIGATION_BARS,
- 0xffffffff & ~(NAVIGATION_BAR_TRANSLUCENT | NAVIGATION_BAR_TRANSPARENT));
- }
-
- void assertContainsAppearance(@Appearance int appearance, int systemUiVisibility) {
- assertTrue((getAppearance(systemUiVisibility) & appearance) == appearance);
- }
-}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index afab7696e87d..8a000a034702 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -124,6 +124,22 @@ public class InsetsStateTest {
}
@Test
+ public void testCalculateInsets_extraNavRightClimateTop() throws Exception {
+ mState.getSource(ITYPE_CLIMATE_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_CLIMATE_BAR).setVisible(true);
+ mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
+ mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
+ WINDOWING_MODE_UNDEFINED, null);
+ // ITYPE_CLIMATE_BAR is a type of status bar and ITYPE_EXTRA_NAVIGATION_BAR is a type
+ // of navigation bar.
+ assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
+ assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
+ }
+
+ @Test
public void testCalculateInsets_imeIgnoredWithoutAdjustResize() {
mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
@@ -336,6 +352,8 @@ public class InsetsStateTest {
public void testGetDefaultVisibility() {
assertTrue(InsetsState.getDefaultVisibility(ITYPE_STATUS_BAR));
assertTrue(InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR));
+ assertTrue(InsetsState.getDefaultVisibility(ITYPE_CLIMATE_BAR));
+ assertTrue(InsetsState.getDefaultVisibility(ITYPE_EXTRA_NAVIGATION_BAR));
assertTrue(InsetsState.getDefaultVisibility(ITYPE_CAPTION_BAR));
assertFalse(InsetsState.getDefaultVisibility(ITYPE_IME));
}
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 5c410878c99d..92fb52837c36 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -17,16 +17,23 @@
package android.view.inputmethod;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
import android.annotation.Nullable;
+import android.graphics.BlurMaskFilter;
import android.os.Parcel;
import android.os.UserHandle;
+import android.text.Spannable;
+import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
+import android.text.style.MaskFilterSpan;
+import android.text.style.UnderlineSpan;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -280,6 +287,55 @@ public class EditorInfoTest {
editorInfo.getInitialTextBeforeCursor(/* length= */ 60, /* flags= */ 1);
}
+ @Test
+ public void testSpanAfterSurroundingTextRetrieval() {
+ final int flags = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE;
+ final SpannableStringBuilder sb =
+ new SpannableStringBuilder("ParcelableSpan and non-ParcelableSpan test");
+ final int parcelableStart = 0;
+ final int parcelableEnd = 14;
+ final int nonParcelableStart = 19;
+ final int nonParcelableEnd = 37;
+ final UnderlineSpan parcelableSpan = new UnderlineSpan();
+ final MaskFilterSpan nonParcelableSpan =
+ new MaskFilterSpan(new BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL));
+
+ // Set spans
+ sb.setSpan(parcelableSpan, parcelableStart, parcelableEnd, flags);
+ sb.setSpan(nonParcelableSpan, nonParcelableStart, nonParcelableEnd, flags);
+
+ Object[] spansBefore = sb.getSpans(/* queryStart= */ 0, sb.length(), Object.class);
+ Object[] parcelableSpanBefore = sb.getSpans(parcelableStart, parcelableEnd, Object.class);
+
+ // Verify the original spans length is 2, include ParcelableSpan and non-ParcelableSpan.
+ assertNotNull(spansBefore);
+ assertEquals(2, spansBefore.length);
+
+ // Set initial surrounding text then retrieve the text.
+ EditorInfo editorInfo = new EditorInfo();
+ editorInfo.initialSelStart = sb.length();
+ editorInfo.initialSelEnd = sb.length();
+ editorInfo.inputType = EditorInfo.TYPE_CLASS_TEXT;
+ editorInfo.setInitialSurroundingText(sb);
+ SpannableString textBeforeCursor =
+ (SpannableString) editorInfo.getInitialTextBeforeCursor(
+ /* length= */ 60, /* flags= */ 1);
+
+ Object[] spansAfter =
+ textBeforeCursor.getSpans(/* queryStart= */ 0, sb.length(), Object.class);
+ Object[] parcelableSpanAfter =
+ textBeforeCursor.getSpans(parcelableStart, parcelableEnd, Object.class);
+ Object[] nonParcelableSpanAfter =
+ textBeforeCursor.getSpans(nonParcelableStart, nonParcelableEnd, Object.class);
+
+ // Verify only remain ParcelableSpan and it's different from the original Span instance.
+ assertNotNull(spansAfter);
+ assertEquals(1, spansAfter.length);
+ assertEquals(1, parcelableSpanAfter.length);
+ assertEquals(0, nonParcelableSpanAfter.length);
+ assertNotEquals(parcelableSpanBefore, parcelableSpanAfter);
+ }
+
private static void assertExpectedTextLength(EditorInfo editorInfo,
@Nullable Integer expectBeforeCursorLength, @Nullable Integer expectSelectionLength,
@Nullable Integer expectAfterCursorLength) {
diff --git a/core/tests/coretests/src/android/widget/TextViewProcessTextTest.java b/core/tests/coretests/src/android/widget/TextViewProcessTextTest.java
index ec8dfcbfbde9..91266f0bbb9e 100644
--- a/core/tests/coretests/src/android/widget/TextViewProcessTextTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewProcessTextTest.java
@@ -16,20 +16,10 @@
package android.widget;
-import static android.widget.RichContentReceiver.SOURCE_PROCESS_TEXT;
-
import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.Activity;
import android.app.Instrumentation;
-import android.content.ClipData;
-import android.content.ClipDescription;
import android.content.Intent;
import android.text.Selection;
import android.text.Spannable;
@@ -44,10 +34,6 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mockito;
-
-import java.util.Set;
/**
* Tests for {@link Intent#ACTION_PROCESS_TEXT} functionality in {@link TextView}.
@@ -78,9 +64,6 @@ public class TextViewProcessTextTest {
mTextView.setTextIsSelectable(true);
Selection.setSelection((Spannable) mTextView.getText(), 0, mTextView.getText().length());
- MockReceiverWrapper mockReceiverWrapper = new MockReceiverWrapper();
- mTextView.setRichContentReceiver(mockReceiverWrapper);
-
// We need to run this in the UI thread, as it will create a Toast.
mActivityRule.runOnUiThread(() -> {
triggerOnActivityResult(Activity.RESULT_OK, "Text is replaced.");
@@ -89,46 +72,6 @@ public class TextViewProcessTextTest {
// This is a TextView, which can't be modified. Hence no change should have been made.
assertEquals(originalText, mTextView.getText().toString());
- verifyZeroInteractions(mockReceiverWrapper.mMock);
- }
-
- @Test
- public void testProcessTextActivityResultEditable_defaultRichContentReceiver()
- throws Throwable {
- mActivityRule.runOnUiThread(() -> mTextView = new EditText(mActivity));
- mInstrumentation.waitForIdleSync();
- CharSequence originalText = "This is some text.";
- mTextView.setText(originalText, BufferType.SPANNABLE);
- assertEquals(originalText, mTextView.getText().toString());
- mTextView.setTextIsSelectable(true);
- Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length());
-
- CharSequence newText = "Text is replaced.";
- triggerOnActivityResult(Activity.RESULT_OK, newText);
-
- assertEquals(newText, mTextView.getText().toString());
- }
-
- @Test
- public void testProcessTextActivityResultEditable_customRichContentReceiver() throws Throwable {
- mActivityRule.runOnUiThread(() -> mTextView = new EditText(mActivity));
- mInstrumentation.waitForIdleSync();
- CharSequence originalText = "This is some text.";
- mTextView.setText(originalText, BufferType.SPANNABLE);
- assertEquals(originalText, mTextView.getText().toString());
- mTextView.setTextIsSelectable(true);
- Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length());
-
- MockReceiverWrapper mockReceiverWrapper = new MockReceiverWrapper();
- mTextView.setRichContentReceiver(mockReceiverWrapper);
-
- CharSequence newText = "Text is replaced.";
- triggerOnActivityResult(Activity.RESULT_OK, newText);
-
- ClipData expectedClip = ClipData.newPlainText("", newText);
- verify(mockReceiverWrapper.mMock, times(1)).onReceive(
- eq(mTextView), clipEq(expectedClip), eq(SOURCE_PROCESS_TEXT), eq(0));
- verifyNoMoreInteractions(mockReceiverWrapper.mMock);
}
@Test
@@ -141,14 +84,10 @@ public class TextViewProcessTextTest {
mTextView.setTextIsSelectable(true);
Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length());
- MockReceiverWrapper mockReceiverWrapper = new MockReceiverWrapper();
- mTextView.setRichContentReceiver(mockReceiverWrapper);
-
CharSequence newText = "Text is replaced.";
triggerOnActivityResult(Activity.RESULT_CANCELED, newText);
assertEquals(originalText, mTextView.getText().toString());
- verifyZeroInteractions(mockReceiverWrapper.mMock);
}
@Test
@@ -161,13 +100,9 @@ public class TextViewProcessTextTest {
mTextView.setTextIsSelectable(true);
Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length());
- MockReceiverWrapper mockReceiverWrapper = new MockReceiverWrapper();
- mTextView.setRichContentReceiver(mockReceiverWrapper);
-
mTextView.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, null);
assertEquals(originalText, mTextView.getText().toString());
- verifyZeroInteractions(mockReceiverWrapper.mMock);
}
private void triggerOnActivityResult(int resultCode, CharSequence replacementText) {
@@ -175,55 +110,4 @@ public class TextViewProcessTextTest {
data.putExtra(Intent.EXTRA_PROCESS_TEXT, replacementText);
mTextView.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, resultCode, data);
}
-
- // This wrapper is used so that we only mock and verify the public callback methods. In addition
- // to the public methods, the RichContentReceiver interface has some hidden default methods;
- // we don't want to mock or assert calls to these helper functions (they are an implementation
- // detail).
- private static class MockReceiverWrapper implements RichContentReceiver<TextView> {
- private final RichContentReceiver<TextView> mMock;
-
- @SuppressWarnings("unchecked")
- MockReceiverWrapper() {
- this.mMock = Mockito.mock(RichContentReceiver.class);
- }
-
- public RichContentReceiver<TextView> getMock() {
- return mMock;
- }
-
- @Override
- public boolean onReceive(TextView view, ClipData clip, @Source int source,
- @Flags int flags) {
- return mMock.onReceive(view, clip, source, flags);
- }
-
- @Override
- public Set<String> getSupportedMimeTypes() {
- return mMock.getSupportedMimeTypes();
- }
- }
-
- private static ClipData clipEq(ClipData expected) {
- return argThat(new ClipDataArgumentMatcher(expected));
- }
-
- private static class ClipDataArgumentMatcher implements ArgumentMatcher<ClipData> {
- private final ClipData mExpected;
-
- private ClipDataArgumentMatcher(ClipData expected) {
- this.mExpected = expected;
- }
-
- @Override
- public boolean matches(ClipData actual) {
- ClipDescription actualDesc = actual.getDescription();
- ClipDescription expectedDesc = mExpected.getDescription();
- return expectedDesc.getLabel().equals(actualDesc.getLabel())
- && actualDesc.getMimeTypeCount() == 1
- && expectedDesc.getMimeType(0).equals(actualDesc.getMimeType(0))
- && actual.getItemCount() == 1
- && mExpected.getItemAt(0).getText().equals(actual.getItemAt(0).getText());
- }
- }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
new file mode 100644
index 000000000000..403c1c219a52
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Comparator;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelSingleProcessCpuThreadReaderTest {
+
+ private File mProcDirectory;
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getContext();
+ mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mProcDirectory);
+ }
+
+ @Test
+ public void getThreadCpuUsage() throws IOException {
+ setupDirectory(42,
+ new int[] {42, 1, 2, 3},
+ new int[] {1000, 2000},
+ // Units are 10ms aka 10000Us
+ new int[][] {{100, 200}, {0, 200}, {100, 300}, {0, 400}},
+ new int[] {1400, 1500});
+
+ KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(42,
+ mProcDirectory.toPath());
+ KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
+ reader.getProcessCpuUsage();
+ assertThat(processCpuUsage.cpuTimeMillis).isEqualTo(29000);
+ List<KernelSingleProcessCpuThreadReader.ThreadCpuUsage> threadCpuUsage =
+ processCpuUsage.threadCpuUsages;
+ threadCpuUsage.sort(Comparator.comparingInt(o -> o.threadId));
+ assertThat(threadCpuUsage).hasSize(4);
+ assertThat(threadCpuUsage.get(0).threadId).isEqualTo(1);
+ assertThat(threadCpuUsage.get(0).usageTimesMillis).isEqualTo(new long[]{0, 2000});
+ assertThat(threadCpuUsage.get(1).threadId).isEqualTo(2);
+ assertThat(threadCpuUsage.get(1).usageTimesMillis).isEqualTo(new long[]{1000, 3000});
+ assertThat(threadCpuUsage.get(2).threadId).isEqualTo(3);
+ assertThat(threadCpuUsage.get(2).usageTimesMillis).isEqualTo(new long[]{0, 4000});
+ assertThat(threadCpuUsage.get(3).threadId).isEqualTo(42);
+ assertThat(threadCpuUsage.get(3).usageTimesMillis).isEqualTo(new long[]{1000, 2000});
+ }
+
+ @Test
+ public void getCpuFrequencyCount() throws IOException {
+ setupDirectory(13,
+ new int[] {13},
+ new int[] {1000, 2000, 3000},
+ new int[][] {{100, 200, 300}},
+ new int[] {14, 15});
+
+ KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(13,
+ mProcDirectory.toPath());
+ int cpuFrequencyCount = reader.getCpuFrequencyCount();
+ assertThat(cpuFrequencyCount).isEqualTo(3);
+ }
+
+ private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies,
+ int[][] threadCpuTimes, int[] processCpuTimes)
+ throws IOException {
+
+ assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs());
+
+ try (OutputStream timeInStateStream =
+ Files.newOutputStream(
+ mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) {
+ for (int i = 0; i < cpuFrequencies.length; i++) {
+ final String line = cpuFrequencies[i] + " 0\n";
+ timeInStateStream.write(line.getBytes());
+ }
+ }
+
+ Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid));
+
+ // Make /proc/$PID
+ assertTrue(processPath.toFile().mkdirs());
+
+ // Write /proc/$PID/stat. Only the fields 14-17 matter.
+ try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) {
+ timeInStateStream.write(
+ (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 "
+ + processCpuTimes[0] + " "
+ + processCpuTimes[1] + " "
+ + "16 17 18 19 20 ...").getBytes());
+ }
+
+ // Make /proc/$PID/task
+ final Path selfThreadsPath = processPath.resolve("task");
+ assertTrue(selfThreadsPath.toFile().mkdirs());
+
+ // Make thread directories
+ for (int i = 0; i < threadIds.length; i++) {
+ // Make /proc/$PID/task/$TID
+ final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
+ assertTrue(threadPath.toFile().mkdirs());
+
+ // Make /proc/$PID/task/$TID/time_in_state
+ try (OutputStream timeInStateStream =
+ Files.newOutputStream(threadPath.resolve("time_in_state"))) {
+ for (int j = 0; j < cpuFrequencies.length; j++) {
+ final String line = cpuFrequencies[j] + " " + threadCpuTimes[i][j] + "\n";
+ timeInStateStream.write(line.getBytes());
+ }
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
index 10ba54865dbe..121c637310c6 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
@@ -55,82 +55,107 @@ public class SystemServerCpuThreadReaderTest {
@Test
public void testReaderDelta_firstTime() throws IOException {
- int uid = 42;
+ int pid = 42;
setupDirectory(
- mProcDirectory.toPath().resolve(String.valueOf(uid)),
- new int[]{42, 1, 2, 3},
- new int[]{1000, 2000},
+ pid,
+ new int[] {42, 1, 2, 3},
+ new int[] {1000, 2000},
// Units are 10ms aka 10000Us
- new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}});
+ new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}},
+ new int[] {1400, 1500});
SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
- mProcDirectory.toPath(), uid);
- reader.setBinderThreadNativeTids(new int[]{1, 3});
+ mProcDirectory.toPath(), pid);
+ reader.setBinderThreadNativeTids(new int[] {1, 3});
SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
reader.readDelta();
- assertArrayEquals(new long[]{100 * 10000, 1100 * 10000},
+ assertArrayEquals(new long[] {100 * 10000, 1100 * 10000},
systemServiceCpuThreadTimes.threadCpuTimesUs);
- assertArrayEquals(new long[]{0, 600 * 10000},
+ assertArrayEquals(new long[] {0, 600 * 10000},
systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
}
@Test
public void testReaderDelta_nextTime() throws IOException {
- int uid = 42;
+ int pid = 42;
setupDirectory(
- mProcDirectory.toPath().resolve(String.valueOf(uid)),
- new int[]{42, 1, 2, 3},
- new int[]{1000, 2000},
- new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}});
+ pid,
+ new int[] {42, 1, 2, 3},
+ new int[] {1000, 2000},
+ new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}},
+ new int[] {1400, 1500});
SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
- mProcDirectory.toPath(), uid);
- reader.setBinderThreadNativeTids(new int[]{1, 3});
+ mProcDirectory.toPath(), pid);
+ reader.setBinderThreadNativeTids(new int[] {1, 3});
// First time, populate "last" snapshot
reader.readDelta();
FileUtils.deleteContents(mProcDirectory);
setupDirectory(
- mProcDirectory.toPath().resolve(String.valueOf(uid)),
- new int[]{42, 1, 2, 3},
- new int[]{1000, 2000},
- new int[][]{{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}});
+ pid,
+ new int[] {42, 1, 2, 3},
+ new int[] {1000, 2000},
+ new int[][] {{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}},
+ new int[] {2400, 2500});
// Second time, get the actual delta
SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
reader.readDelta();
- assertArrayEquals(new long[]{3100 * 10000, 2500 * 10000},
+ assertArrayEquals(new long[] {3100 * 10000, 2500 * 10000},
systemServiceCpuThreadTimes.threadCpuTimesUs);
- assertArrayEquals(new long[]{1800 * 10000, 1400 * 10000},
+ assertArrayEquals(new long[] {1800 * 10000, 1400 * 10000},
systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
}
- private void setupDirectory(Path processPath, int[] threadIds, int[] cpuFrequencies,
- int[][] cpuTimes) throws IOException {
+ private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies, int[][] cpuTimes,
+ int[] processCpuTimes)
+ throws IOException {
+
+ assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs());
+
+ try (OutputStream timeInStateStream =
+ Files.newOutputStream(
+ mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) {
+ for (int i = 0; i < cpuFrequencies.length; i++) {
+ final String line = cpuFrequencies[i] + " 0\n";
+ timeInStateStream.write(line.getBytes());
+ }
+ }
+
+ Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid));
// Make /proc/$PID
assertTrue(processPath.toFile().mkdirs());
+ // Write /proc/$PID/stat. Only the fields 14-17 matter.
+ try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) {
+ timeInStateStream.write(
+ (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 "
+ + processCpuTimes[0] + " "
+ + processCpuTimes[1] + " "
+ + "16 17 18 19 20 ...").getBytes());
+ }
+
// Make /proc/$PID/task
final Path selfThreadsPath = processPath.resolve("task");
assertTrue(selfThreadsPath.toFile().mkdirs());
- // Make thread directories in reverse order, as they are read in order of creation by
- // CpuThreadProcReader
+ // Make thread directories
for (int i = 0; i < threadIds.length; i++) {
// Make /proc/$PID/task/$TID
final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
assertTrue(threadPath.toFile().mkdirs());
// Make /proc/$PID/task/$TID/time_in_state
- final OutputStream timeInStateStream =
- Files.newOutputStream(threadPath.resolve("time_in_state"));
- for (int j = 0; j < cpuFrequencies.length; j++) {
- final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n";
- timeInStateStream.write(line.getBytes());
+ try (OutputStream timeInStateStream =
+ Files.newOutputStream(threadPath.resolve("time_in_state"))) {
+ for (int j = 0; j < cpuFrequencies.length; j++) {
+ final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n";
+ timeInStateStream.write(line.getBytes());
+ }
}
- timeInStateStream.close();
}
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index 2eee140b921f..37e1efdecfd1 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -43,18 +43,18 @@ public class SystemServicePowerCalculatorTest {
private PowerProfile mProfile;
private MockBatteryStatsImpl mMockBatteryStats;
private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
- private MockServerCpuThreadReader mMockServerCpuThreadReader;
+ private MockSystemServerCpuThreadReader mMockSystemServerCpuThreadReader;
private SystemServicePowerCalculator mSystemServicePowerCalculator;
@Before
public void setUp() throws IOException {
Context context = InstrumentationRegistry.getContext();
mProfile = new PowerProfile(context, true /* forTest */);
- mMockServerCpuThreadReader = new MockServerCpuThreadReader();
+ mMockSystemServerCpuThreadReader = new MockSystemServerCpuThreadReader();
mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader();
mMockBatteryStats = new MockBatteryStatsImpl(new MockClocks())
.setPowerProfile(mProfile)
- .setSystemServerCpuThreadReader(mMockServerCpuThreadReader)
+ .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader)
.setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader)
.setUserInfoProvider(new MockUserInfoProvider());
mMockBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
@@ -65,12 +65,13 @@ public class SystemServicePowerCalculatorTest {
@Test
public void testCalculateApp() {
// Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total
- mMockServerCpuThreadReader.setThreadTimes(
- new long[]{30000, 40000, 50000, 60000, 70000, 80000, 90000},
- new long[]{20000, 30000, 40000, 50000, 60000, 70000, 80000});
+ mMockSystemServerCpuThreadReader.setCpuTimes(
+ 210000,
+ new long[] {30000, 40000, 50000, 60000, 70000, 80000, 90000},
+ new long[] {20000, 30000, 40000, 50000, 60000, 70000, 80000});
mMockCpuUidFreqTimeReader.setSystemServerCpuTimes(
- new long[]{10000, 20000, 30000, 40000, 50000, 60000, 70000}
+ new long[] {10000, 20000, 30000, 40000, 50000, 60000, 70000}
);
mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false);
@@ -106,13 +107,13 @@ public class SystemServicePowerCalculatorTest {
mMockBatteryStats.getUidStatsLocked(workSourceUid1), 0);
mSystemServicePowerCalculator.calculateApp(app1, app1.uidObj, 0, 0,
BatteryStats.STATS_SINCE_CHARGED);
- assertEquals(0.27162, app1.systemServiceCpuPowerMah, 0.00001);
+ assertEquals(0.00018958, app1.systemServiceCpuPowerMah, 0.0000001);
BatterySipper app2 = new BatterySipper(BatterySipper.DrainType.APP,
mMockBatteryStats.getUidStatsLocked(workSourceUid2), 0);
mSystemServicePowerCalculator.calculateApp(app2, app2.uidObj, 0, 0,
BatteryStats.STATS_SINCE_CHARGED);
- assertEquals(2.44458, app2.systemServiceCpuPowerMah, 0.00001);
+ assertEquals(0.00170625, app2.systemServiceCpuPowerMah, 0.0000001);
}
private static class MockKernelCpuUidFreqTimeReader extends
@@ -140,14 +141,16 @@ public class SystemServicePowerCalculatorTest {
}
}
- private static class MockServerCpuThreadReader extends SystemServerCpuThreadReader {
+ private static class MockSystemServerCpuThreadReader extends SystemServerCpuThreadReader {
private SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes();
- MockServerCpuThreadReader() {
+ MockSystemServerCpuThreadReader() {
super(null);
}
- public void setThreadTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) {
+ public void setCpuTimes(long processCpuTimeUs, long[] threadCpuTimesUs,
+ long[] binderThreadCpuTimesUs) {
+ mThreadTimes.processCpuTimeUs = processCpuTimeUs;
mThreadTimes.threadCpuTimesUs = threadCpuTimesUs;
mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs;
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 102c933dd84d..2779a75378b7 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -168,6 +168,7 @@ applications that come with the platform
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS" />
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
<permission name="android.permission.PERFORM_CDMA_PROVISIONING"/>
@@ -440,6 +441,13 @@ applications that come with the platform
<permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.traceur">
+ <!-- Permissions required to receive BUGREPORT_STARTED intent -->
+ <permission name="android.permission.DUMP"/>
+ <!-- Permissions required for quick settings tile -->
+ <permission name="android.permission.STATUS_BAR"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.tv">
<permission name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"/>
<permission name="android.permission.DVB_DEVICE"/>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 0504cdc15b52..bda84dd76cf5 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -283,10 +283,30 @@
<font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
</family>
<family lang="und-Ethi">
- <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
- <font weight="700" style="normal">NotoSansEthiopic-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.otf</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-Bold.otf</font>
+ <font weight="400" style="normal">NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal">NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal">NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal">NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="700" />
+ </font>
</family>
<family lang="und-Hebr">
<font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java
new file mode 100644
index 000000000000..d5243164abdc
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java
@@ -0,0 +1,123 @@
+/*
+ * 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.allOf;
+import static com.google.errorprone.matchers.Matchers.enclosingClass;
+import static com.google.errorprone.matchers.Matchers.enclosingMethod;
+import static com.google.errorprone.matchers.Matchers.instanceMethod;
+import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.methodIsNamed;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * Parcelable data can be transported in many ways (some of which can be very
+ * inefficient) so this checker guides developers towards using high-performance
+ * best-practices.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkParcelablePerformance",
+ summary = "Verifies Parcelable performance best-practices",
+ severity = WARNING)
+public final class ParcelablePerformanceChecker extends BugChecker
+ implements MethodInvocationTreeMatcher {
+ private static final Matcher<Tree> INSIDE_WRITE_TO_PARCEL = allOf(
+ enclosingClass(isSubtypeOf("android.os.Parcelable")),
+ enclosingMethod(methodIsNamed("writeToParcel")));
+
+ private static final Matcher<ExpressionTree> WRITE_STRING = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeString"));
+ private static final Matcher<ExpressionTree> WRITE_STRING_ARRAY = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeStringArray"));
+
+ private static final Matcher<ExpressionTree> WRITE_VALUE = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeValue"));
+ private static final Matcher<ExpressionTree> WRITE_PARCELABLE = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeParcelable"));
+
+ private static final Matcher<ExpressionTree> WRITE_LIST = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeList"));
+ private static final Matcher<ExpressionTree> WRITE_PARCELABLE_LIST = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeParcelableList"));
+ private static final Matcher<ExpressionTree> WRITE_PARCELABLE_ARRAY = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeParcelableArray"));
+
+ @Override
+ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+ if (INSIDE_WRITE_TO_PARCEL.matches(tree, state)) {
+ if (WRITE_STRING.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use 'writeString8()' to improve "
+ + "efficiency; sending as UTF-8 can double throughput")
+ .build();
+ }
+ if (WRITE_STRING_ARRAY.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use 'writeString8Array()' to improve "
+ + "efficiency; sending as UTF-8 can double throughput")
+ .build();
+ }
+
+ if (WRITE_VALUE.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use strongly-typed methods to improve "
+ + "efficiency; saves 4 bytes for type and overhead of "
+ + "Parcelable class name")
+ .build();
+ }
+ if (WRITE_PARCELABLE.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use 'item.writeToParcel()' to improve "
+ + "efficiency; saves overhead of Parcelable class name")
+ .build();
+ }
+
+ if (WRITE_LIST.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use 'writeTypedList()' to improve "
+ + "efficiency; saves overhead of repeated Parcelable class name")
+ .build();
+ }
+ if (WRITE_PARCELABLE_LIST.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use 'writeTypedList()' to improve "
+ + "efficiency; saves overhead of repeated Parcelable class name")
+ .build();
+ }
+ if (WRITE_PARCELABLE_ARRAY.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use 'writeTypedArray()' to improve "
+ + "efficiency; saves overhead of repeated Parcelable class name")
+ .build();
+ }
+ }
+ return Description.NO_MATCH;
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java
new file mode 100644
index 000000000000..75c76e32f00e
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ParcelablePerformanceCheckerTest {
+ private CompilationTestHelper compilationHelper;
+
+ @Before
+ public void setUp() {
+ compilationHelper = CompilationTestHelper.newInstance(
+ ParcelablePerformanceChecker.class, getClass());
+ }
+
+ @Test
+ public void testString() {
+ compilationHelper
+ .addSourceFile("/android/os/Parcel.java")
+ .addSourceFile("/android/os/Parcelable.java")
+ .addSourceLines("FooInfo.java",
+ "import android.os.Parcel;",
+ "import android.os.Parcelable;",
+ "public class FooInfo implements Parcelable {",
+ " public void writeToParcel(Parcel dest, int flags) {",
+ " // BUG: Diagnostic contains:",
+ " dest.writeString(toString());",
+ " dest.writeString8(toString());",
+ " // BUG: Diagnostic contains:",
+ " dest.writeStringArray(new String[0]);",
+ " dest.writeString8Array(new String[0]);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testSingle() {
+ compilationHelper
+ .addSourceFile("/android/os/Parcel.java")
+ .addSourceFile("/android/os/Parcelable.java")
+ .addSourceLines("FooInfo.java",
+ "import android.os.Parcel;",
+ "import android.os.Parcelable;",
+ "public class FooInfo implements Parcelable {",
+ " public void writeToParcel(Parcel dest, int flags) {",
+ " // BUG: Diagnostic contains:",
+ " dest.writeValue(this);",
+ " this.writeToParcel(dest, flags);",
+ " // BUG: Diagnostic contains:",
+ " dest.writeParcelable(this, flags);",
+ " this.writeToParcel(dest, flags);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testList() {
+ compilationHelper
+ .addSourceFile("/android/os/Parcel.java")
+ .addSourceFile("/android/os/Parcelable.java")
+ .addSourceLines("FooInfo.java",
+ "import android.os.Parcel;",
+ "import android.os.Parcelable;",
+ "import java.util.List;",
+ "import java.util.ArrayList;",
+ "public class FooInfo implements Parcelable {",
+ " public void writeToParcel(Parcel dest, int flags) {",
+ " List<Parcelable> list = new ArrayList<Parcelable>();",
+ " Parcelable[] array = new Parcelable[0];",
+ " // BUG: Diagnostic contains:",
+ " dest.writeList(list);",
+ " dest.writeTypedList(list, flags);",
+ " // BUG: Diagnostic contains:",
+ " dest.writeParcelableList(list, flags);",
+ " dest.writeTypedList(list, flags);",
+ " // BUG: Diagnostic contains:",
+ " dest.writeParcelableArray(array, flags);",
+ " dest.writeTypedArray(array, flags);",
+ " }",
+ "}")
+ .doTest();
+ }
+}
diff --git a/errorprone/tests/res/android/os/Parcel.java b/errorprone/tests/res/android/os/Parcel.java
new file mode 100644
index 000000000000..bafa23626fb2
--- /dev/null
+++ b/errorprone/tests/res/android/os/Parcel.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import java.util.List;
+
+public class Parcel {
+ public void writeString(String val) {
+ throw new UnsupportedOperationException();
+ }
+ public void writeString8(String val) {
+ throw new UnsupportedOperationException();
+ }
+ public final void writeStringArray(String[] val) {
+ throw new UnsupportedOperationException();
+ }
+ public final void writeString8Array(String[] val) {
+ throw new UnsupportedOperationException();
+ }
+
+ public final void writeValue(Object v) {
+ throw new UnsupportedOperationException();
+ }
+ public final void writeParcelable(Parcelable p, int flags) {
+ throw new UnsupportedOperationException();
+ }
+
+ public final void writeList(List val) {
+ throw new UnsupportedOperationException();
+ }
+ public final <T extends Parcelable> void writeParcelableList(List<T> val, int flags) {
+ throw new UnsupportedOperationException();
+ }
+ public <T extends Parcelable> void writeTypedList(List<T> val, int flags) {
+ throw new UnsupportedOperationException();
+ }
+ public final <T extends Parcelable> void writeParcelableArray(T[] value, int flags) {
+ throw new UnsupportedOperationException();
+ }
+ public final <T extends Parcelable> void writeTypedArray(T[] val, int flags) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/errorprone/tests/res/android/os/Parcelable.java b/errorprone/tests/res/android/os/Parcelable.java
new file mode 100644
index 000000000000..217690d69fd6
--- /dev/null
+++ b/errorprone/tests/res/android/os/Parcelable.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+public interface Parcelable {
+ public void writeToParcel(Parcel dest, int flags);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
index 9047b71253da..b275331fa953 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -22,18 +22,18 @@ import android.util.Slog;
import android.view.SurfaceControl;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FullscreenTaskOrg";
- private final TransactionPool mTransactionPool;
+ private final SyncTransactionQueue mSyncQueue;
private final ArraySet<Integer> mTasks = new ArraySet<>();
- FullscreenTaskListener(TransactionPool transactionPool) {
- mTransactionPool = transactionPool;
+ FullscreenTaskListener(SyncTransactionQueue syncQueue) {
+ mSyncQueue = syncQueue;
}
@Override
@@ -42,18 +42,18 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
if (mTasks.contains(taskInfo.taskId)) {
throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
}
- mTasks.add(taskInfo.taskId);
- final SurfaceControl.Transaction t = mTransactionPool.acquire();
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
taskInfo.taskId);
- // Reset several properties back to fullscreen (PiP, for example, leaves all these
- // properties in a bad state).
- t.setPosition(leash, 0, 0);
- t.setWindowCrop(leash, null);
- t.setAlpha(leash, 1f);
- t.setMatrix(leash, 1, 0, 0, 1);
- t.show(leash);
- t.apply();
+ mTasks.add(taskInfo.taskId);
+ mSyncQueue.runInSync(t -> {
+ // Reset several properties back to fullscreen (PiP, for example, leaves all these
+ // properties in a bad state).
+ t.setPosition(leash, 0, 0);
+ t.setWindowCrop(leash, null);
+ t.setAlpha(leash, 1f);
+ t.setMatrix(leash, 1, 0, 0, 1);
+ t.show(leash);
+ });
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 2d82fb1d3a21..d650a958a203 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -28,7 +28,7 @@ import android.window.TaskOrganizer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
@@ -59,16 +59,16 @@ public class ShellTaskOrganizer extends TaskOrganizer {
// require us to report to both old and new listeners)
private final SparseArray<Pair<RunningTaskInfo, SurfaceControl>> mTasks = new SparseArray<>();
- public ShellTaskOrganizer(TransactionPool transactionPool) {
+ public ShellTaskOrganizer(SyncTransactionQueue syncQueue) {
super();
- addListener(new FullscreenTaskListener(transactionPool), WINDOWING_MODE_FULLSCREEN);
+ addListener(new FullscreenTaskListener(syncQueue), WINDOWING_MODE_FULLSCREEN);
}
@VisibleForTesting
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController,
- TransactionPool transactionPool) {
+ SyncTransactionQueue syncQueue) {
super(taskOrganizerController);
- addListener(new FullscreenTaskListener(transactionPool), WINDOWING_MODE_FULLSCREEN);
+ addListener(new FullscreenTaskListener(syncQueue), WINDOWING_MODE_FULLSCREEN);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index c18e9ce76153..d810fb8257a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -43,6 +43,7 @@ import android.view.animation.PathInterpolator;
import com.android.internal.view.IInputMethodManager;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
/**
* Manages IME control at the display-level. This occurs when IME comes up in multi-window mode.
@@ -62,15 +63,21 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
private static final int FLOATING_IME_BOTTOM_INSET = -80;
protected final IWindowManager mWmService;
- protected final Handler mHandler;
+ protected final Executor mExecutor;
private final TransactionPool mTransactionPool;
private final DisplayController mDisplayController;
private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
+ @Deprecated
public DisplayImeController(IWindowManager wmService, DisplayController displayController,
Handler mainHandler, TransactionPool transactionPool) {
- mHandler = mainHandler;
+ this(wmService, displayController, mainHandler::post, transactionPool);
+ }
+
+ public DisplayImeController(IWindowManager wmService, DisplayController displayController,
+ Executor mainExecutor, TransactionPool transactionPool) {
+ mExecutor = mainExecutor;
mWmService = wmService;
mTransactionPool = transactionPool;
mDisplayController = displayController;
@@ -197,7 +204,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
@Override
public void insetsChanged(InsetsState insetsState) {
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (mInsetsState.equals(insetsState)) {
return;
}
@@ -224,15 +231,25 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
continue;
}
if (activeControl.getType() == InsetsState.ITYPE_IME) {
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
final Point lastSurfacePosition = mImeSourceControl != null
? mImeSourceControl.getSurfacePosition() : null;
+ final boolean positionChanged =
+ !activeControl.getSurfacePosition().equals(lastSurfacePosition);
+ final boolean leashChanged =
+ !haveSameLeash(mImeSourceControl, activeControl);
mImeSourceControl = activeControl;
- if (!activeControl.getSurfacePosition().equals(lastSurfacePosition)
- && mAnimation != null) {
- startAnimation(mImeShowing, true /* forceRestart */);
- } else if (!mImeShowing) {
- removeImeSurface();
+ if (mAnimation != null) {
+ if (positionChanged) {
+ startAnimation(mImeShowing, true /* forceRestart */);
+ }
+ } else {
+ if (leashChanged) {
+ applyVisibilityToLeash();
+ }
+ if (!mImeShowing) {
+ removeImeSurface();
+ }
}
});
}
@@ -240,13 +257,27 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
+ private void applyVisibilityToLeash() {
+ SurfaceControl leash = mImeSourceControl.getLeash();
+ if (leash != null) {
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
+ if (mImeShowing) {
+ t.show(leash);
+ } else {
+ t.hide(leash);
+ }
+ t.apply();
+ mTransactionPool.release(t);
+ }
+ }
+
@Override
public void showInsets(int types, boolean fromIme) {
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
- mHandler.post(() -> startAnimation(true /* show */, false /* forceRestart */));
+ mExecutor.execute(() -> startAnimation(true /* show */, false /* forceRestart */));
}
@Override
@@ -255,7 +286,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
return;
}
if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
- mHandler.post(() -> startAnimation(false /* show */, false /* forceRestart */));
+ mExecutor.execute(() -> startAnimation(false /* show */, false /* forceRestart */));
}
@Override
@@ -495,4 +526,20 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
return IInputMethodManager.Stub.asInterface(
ServiceManager.getService(Context.INPUT_METHOD_SERVICE));
}
+
+ private static boolean haveSameLeash(InsetsSourceControl a, InsetsSourceControl b) {
+ if (a == b) {
+ return true;
+ }
+ if (a == null || b == null) {
+ return false;
+ }
+ if (a.getLeash() == b.getLeash()) {
+ return true;
+ }
+ if (a.getLeash() == null || b.getLeash() == null) {
+ return false;
+ }
+ return a.getLeash().isSameSurface(b.getLeash());
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index b4620e27e68c..84b98f9e8047 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -271,14 +271,14 @@ public class SystemWindows {
}
@Override
- public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int relayout(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
long frameNumber, ClientWindowFrames outFrames,
MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
SurfaceControl outBLASTSurfaceControl) {
- int res = super.relayout(window, seq, attrs, requestedWidth, requestedHeight,
+ int res = super.relayout(window, attrs, requestedWidth, requestedHeight,
viewVisibility, flags, frameNumber, outFrames,
mergedConfiguration, outSurfaceControl, outInsetsState,
outActiveControls, outSurfaceSize, outBLASTSurfaceControl);
@@ -365,10 +365,6 @@ public class SystemWindows {
public void updatePointerIcon(float x, float y) {}
@Override
- public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
- int localValue, int localChanges) {}
-
- @Override
public void dispatchWindowShown() {}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 9954618134e8..6bc838fcc8be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -239,9 +239,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
throw new RuntimeException("Callers should call scheduleOffset() instead of this "
+ "directly");
}
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
mDisplayAreaMap.forEach(
- (key, leash) -> animateWindows(leash, fromBounds, toBounds, direction,
- durationMs));
+ (key, leash) -> {
+ animateWindows(leash, fromBounds, toBounds, direction,
+ durationMs);
+ wct.setBounds(key.token, toBounds);
+ });
+ applyTransaction(wct);
}
private void resetWindowsOffset() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index 7c0c738644b7..b6b518d69c55 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -184,9 +184,8 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
mDisplaySize.x, mTutorialAreaHeight, 0, 0,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSLUCENT);
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
lp.gravity = Gravity.TOP | Gravity.LEFT;
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setFitInsetsTypes(0 /* types */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java
index edbbd69ce0dc..cdb27da7de07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java
@@ -1319,34 +1319,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mBackground.getRight(), mBackground.getBottom(), Op.UNION);
}
- void onDockedTopTask() {
- mState.animateAfterRecentsDrawn = true;
- startDragging(false /* animate */, false /* touching */);
- updateDockSide();
- mEntranceAnimationRunning = true;
-
- resizeStackSurfaces(calculatePositionForInsetBounds(),
- mSplitLayout.getSnapAlgorithm().getMiddleTarget().position,
- mSplitLayout.getSnapAlgorithm().getMiddleTarget(),
- null /* transaction */);
- }
-
- void onRecentsDrawn() {
- updateDockSide();
- final int position = calculatePositionForInsetBounds();
- if (mState.animateAfterRecentsDrawn) {
- mState.animateAfterRecentsDrawn = false;
-
- mHandler.post(() -> {
- // Delay switching resizing mode because this might cause jank in recents animation
- // that's longer than this animation.
- stopDragging(position, getSnapAlgorithm().getMiddleTarget(),
- mLongPressEntraceAnimDuration, Interpolators.FAST_OUT_SLOW_IN,
- 200 /* endDelay */);
- });
- }
- }
-
void onUndockingTask() {
int dockSide = mSplitLayout.getPrimarySplitSide();
if (inSplitMode()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 58106c94a000..985dff20ad32 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -48,14 +48,6 @@ public interface SplitScreen {
/** Switch to minimized state if appropriate. */
void setMinimized(boolean minimized);
- /**
- * Workaround for b/62528361, at the time recents has drawn, it may happen before a
- * configuration change to the Divider, and internally, the event will be posted to the
- * subscriber, or DividerView, which has been removed and prevented from resizing. Instead,
- * register the event handler here and proxy the event to the current DividerView.
- */
- void onRecentsDrawn();
-
/** Called when there's an activity forced resizable. */
void onActivityForcedResizable(String packageName, int taskId, int reason);
@@ -68,9 +60,6 @@ public interface SplitScreen {
/** Called when there's a task undocking. */
void onUndockingTask();
- /** Called when top task docked. */
- void onDockedTopTask();
-
/** Called when app transition finished. */
void onAppTransitionFinished();
@@ -85,4 +74,13 @@ public interface SplitScreen {
/** @return the container token for the secondary split root task. */
WindowContainerToken getSecondaryRoot();
+
+ /**
+ * Splits the primary task if feasible, this is to preserve legacy way to toggle split screen.
+ * Like triggering split screen through long pressing recents app button or through
+ * {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN}.
+ *
+ * @return {@code true} if it successes to split the primary task.
+ */
+ boolean splitPrimaryTask();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index c8be93747f19..43e4d62baaf6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -16,19 +16,25 @@
package com.android.wm.shell.splitscreen;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.Display.DEFAULT_DISPLAY;
+import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
+import android.os.RemoteException;
import android.provider.Settings;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.Toast;
import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -40,12 +46,14 @@ import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -56,7 +64,7 @@ public class SplitScreenController implements SplitScreen,
DisplayController.OnDisplaysChangedListener {
static final boolean DEBUG = false;
- private static final String TAG = "Divider";
+ private static final String TAG = "SplitScreenCtrl";
private static final int DEFAULT_APP_TRANSITION_DURATION = 336;
private final Context mContext;
@@ -99,7 +107,7 @@ public class SplitScreenController implements SplitScreen,
public SplitScreenController(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController imeController, Handler handler, TransactionPool transactionPool,
- ShellTaskOrganizer shellTaskOrganizer) {
+ ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue) {
mContext = context;
mDisplayController = displayController;
mSystemWindows = systemWindows;
@@ -107,8 +115,7 @@ public class SplitScreenController implements SplitScreen,
mHandler = handler;
mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
mTransactionPool = transactionPool;
- mWindowManagerProxy = new WindowManagerProxy(mTransactionPool, mHandler,
- shellTaskOrganizer);
+ mWindowManagerProxy = new WindowManagerProxy(syncQueue, shellTaskOrganizer);
mTaskOrganizer = shellTaskOrganizer;
mSplits = new SplitScreenTaskOrganizer(this, shellTaskOrganizer);
mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler,
@@ -157,12 +164,12 @@ public class SplitScreenController implements SplitScreen,
// Don't initialize the divider or anything until we get the default display.
}
- /** Returns {@code true} if split screen is supported on the device. */
+ @Override
public boolean isSplitScreenSupported() {
return mSplits.isSplitScreenSupported();
}
- /** Called when keyguard showing state changed. */
+ @Override
public void onKeyguardVisibilityChanged(boolean showing) {
if (!isSplitActive() || mView == null) {
return;
@@ -229,21 +236,22 @@ public class SplitScreenController implements SplitScreen,
mHandler.post(task);
}
- /** Returns {@link DividerView}. */
+ @Override
public DividerView getDividerView() {
return mView;
}
- /** Returns {@code true} if one of the split screen is in minimized mode. */
+ @Override
public boolean isMinimized() {
return mMinimized;
}
+ @Override
public boolean isHomeStackResizable() {
return mHomeStackResizable;
}
- /** Returns {@code true} if the divider is visible. */
+ @Override
public boolean isDividerVisible() {
return mView != null && mView.getVisibility() == View.VISIBLE;
}
@@ -328,7 +336,7 @@ public class SplitScreenController implements SplitScreen,
}
}
- /** Switch to minimized state if appropriate. */
+ @Override
public void setMinimized(final boolean minimized) {
if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
mHandler.post(() -> {
@@ -392,48 +400,29 @@ public class SplitScreenController implements SplitScreen,
mWindowManager.setTouchable(!mAdjustedForIme);
}
- /**
- * Workaround for b/62528361, at the time recents has drawn, it may happen before a
- * configuration change to the Divider, and internally, the event will be posted to the
- * subscriber, or DividerView, which has been removed and prevented from resizing. Instead,
- * register the event handler here and proxy the event to the current DividerView.
- */
- public void onRecentsDrawn() {
- if (mView != null) {
- mView.onRecentsDrawn();
- }
- }
-
- /** Called when there's an activity forced resizable. */
+ @Override
public void onActivityForcedResizable(String packageName, int taskId, int reason) {
mForcedResizableController.activityForcedResizable(packageName, taskId, reason);
}
- /** Called when there's an activity dismissing split screen. */
+ @Override
public void onActivityDismissingSplitScreen() {
mForcedResizableController.activityDismissingSplitScreen();
}
- /** Called when there's an activity launch on secondary display failed. */
+ @Override
public void onActivityLaunchOnSecondaryDisplayFailed() {
mForcedResizableController.activityLaunchOnSecondaryDisplayFailed();
}
- /** Called when there's a task undocking. */
+ @Override
public void onUndockingTask() {
if (mView != null) {
mView.onUndockingTask();
}
}
- /** Called when top task docked. */
- public void onDockedTopTask() {
- if (mView != null) {
- mView.onDockedTopTask();
- }
- }
-
- /** Called when app transition finished. */
+ @Override
public void onAppTransitionFinished() {
if (mView == null) {
return;
@@ -441,7 +430,7 @@ public class SplitScreenController implements SplitScreen,
mForcedResizableController.onAppTransitionFinished();
}
- /** Dumps current status of Split Screen. */
+ @Override
public void dump(PrintWriter pw) {
pw.print(" mVisible="); pw.println(mVisible);
pw.print(" mMinimized="); pw.println(mMinimized);
@@ -458,7 +447,7 @@ public class SplitScreenController implements SplitScreen,
return (long) (transitionDuration * transitionScale);
}
- /** Registers listener that gets called whenever the existence of the divider changes. */
+ @Override
public void registerInSplitScreenListener(Consumer<Boolean> listener) {
listener.accept(isDividerVisible());
synchronized (mDockedStackExistsListeners) {
@@ -473,6 +462,42 @@ public class SplitScreenController implements SplitScreen,
}
}
+ @Override
+ public boolean splitPrimaryTask() {
+ try {
+ if (ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED
+ || isSplitActive()) {
+ return false;
+ }
+
+ // Try fetching the top running task.
+ final List<RunningTaskInfo> runningTasks =
+ ActivityTaskManager.getService().getTasks(1 /* maxNum */);
+ if (runningTasks == null || runningTasks.isEmpty()) {
+ return false;
+ }
+ // Note: The set of running tasks from the system is ordered by recency.
+ final RunningTaskInfo topRunningTask = runningTasks.get(0);
+
+ final int activityType = topRunningTask.configuration.windowConfiguration
+ .getActivityType();
+ if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
+ return false;
+ }
+
+ if (!topRunningTask.supportsSplitScreenMultiWindow) {
+ Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
+ Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
+ return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(
+ topRunningTask.taskId, true /* onTop */);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
/** Notifies the bounds of split screen changed. */
void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) {
synchronized (mBoundsChangedListeners) {
@@ -532,7 +557,7 @@ public class SplitScreenController implements SplitScreen,
return mWindowManagerProxy;
}
- /** @return the container token for the secondary split root task. */
+ @Override
public WindowContainerToken getSecondaryRoot() {
if (mSplits == null || mSplits.mSecondary == null) {
return null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
index 25827cdb9e24..47e7c99d2268 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
@@ -28,7 +28,6 @@ import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.graphics.Rect;
-import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
@@ -41,7 +40,6 @@ import android.window.WindowOrganizer;
import com.android.internal.annotations.GuardedBy;
import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TransactionPool;
import java.util.ArrayList;
import java.util.List;
@@ -85,9 +83,8 @@ class WindowManagerProxy {
private final TaskOrganizer mTaskOrganizer;
- WindowManagerProxy(TransactionPool transactionPool, Handler handler,
- TaskOrganizer taskOrganizer) {
- mSyncTransactionQueue = new SyncTransactionQueue(transactionPool, handler);
+ WindowManagerProxy(SyncTransactionQueue syncQueue, TaskOrganizer taskOrganizer) {
+ mSyncTransactionQueue = syncQueue;
mTaskOrganizer = taskOrganizer;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 7b499d4d6e7d..823e0b7f03c5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -33,7 +33,7 @@ import android.window.ITaskOrganizerController;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.SyncTransactionQueue;
import org.junit.Before;
import org.junit.Test;
@@ -54,7 +54,7 @@ public class ShellTaskOrganizerTests {
private ITaskOrganizerController mTaskOrganizerController;
ShellTaskOrganizer mOrganizer;
- private final TransactionPool mTransactionPool = mock(TransactionPool.class);
+ private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class);
private class TrackingTaskListener implements ShellTaskOrganizer.TaskListener {
final ArrayList<RunningTaskInfo> appeared = new ArrayList<>();
@@ -85,7 +85,7 @@ public class ShellTaskOrganizerTests {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController, mTransactionPool);
+ mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController, mSyncTransactionQueue);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
new file mode 100644
index 000000000000..080cddc58a09
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.wm.shell.common;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.InsetsState.ITYPE_IME;
+import static android.view.Surface.ROTATION_0;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.graphics.Point;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.view.IInputMethodManager;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+public class DisplayImeControllerTest {
+
+ private SurfaceControl.Transaction mT;
+ private DisplayImeController.PerDisplay mPerDisplay;
+ private IInputMethodManager mMock;
+
+ @Before
+ public void setUp() throws Exception {
+ mT = mock(SurfaceControl.Transaction.class);
+ mMock = mock(IInputMethodManager.class);
+ mPerDisplay = new DisplayImeController(null, null, Runnable::run, new TransactionPool() {
+ @Override
+ public SurfaceControl.Transaction acquire() {
+ return mT;
+ }
+
+ @Override
+ public void release(SurfaceControl.Transaction t) {
+ }
+ }) {
+ @Override
+ public IInputMethodManager getImms() {
+ return mMock;
+ }
+ }.new PerDisplay(DEFAULT_DISPLAY, ROTATION_0);
+ }
+
+ @Test
+ public void reappliesVisibilityToChangedLeash() {
+ verifyZeroInteractions(mT);
+
+ mPerDisplay.mImeShowing = false;
+ mPerDisplay.insetsControlChanged(new InsetsState(), new InsetsSourceControl[] {
+ new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0))
+ });
+
+ verify(mT).hide(any());
+
+ mPerDisplay.mImeShowing = true;
+ mPerDisplay.insetsControlChanged(new InsetsState(), new InsetsSourceControl[] {
+ new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0))
+ });
+
+ verify(mT).show(any());
+ }
+}
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 6f05cbd0ebb3..71c8e1f6121f 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -30,23 +30,62 @@
namespace android {
-CursorWindow::CursorWindow(const String8& name, int ashmemFd,
- void* data, size_t size, bool readOnly) :
- mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) {
+/**
+ * By default windows are lightweight inline allocations of this size;
+ * they're only inflated to ashmem regions when more space is needed.
+ */
+static constexpr const size_t kInlineSize = 16384;
+
+CursorWindow::CursorWindow(const String8& name, int ashmemFd, void* data, size_t size,
+ size_t inflatedSize, bool readOnly) :
+ mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size),
+ mInflatedSize(inflatedSize), mReadOnly(readOnly) {
mHeader = static_cast<Header*>(mData);
}
CursorWindow::~CursorWindow() {
- ::munmap(mData, mSize);
- ::close(mAshmemFd);
+ if (mAshmemFd != -1) {
+ ::munmap(mData, mSize);
+ ::close(mAshmemFd);
+ } else {
+ free(mData);
+ }
+}
+
+status_t CursorWindow::create(const String8& name, size_t inflatedSize,
+ CursorWindow** outCursorWindow) {
+ *outCursorWindow = nullptr;
+
+ size_t size = std::min(kInlineSize, inflatedSize);
+ void* data = calloc(size, 1);
+ if (!data) return NO_MEMORY;
+
+ CursorWindow* window = new CursorWindow(name, -1, data, size,
+ inflatedSize, false /*readOnly*/);
+ status_t result = window->clear();
+ if (!result) {
+ LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
+ "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
+ window->mHeader->freeOffset,
+ window->mHeader->numRows,
+ window->mHeader->numColumns,
+ window->mSize, window->mData);
+ *outCursorWindow = window;
+ return OK;
+ }
+ delete window;
+ return result;
}
-status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) {
+status_t CursorWindow::inflate() {
+ // Shortcut when we can't expand any further
+ if (mSize == mInflatedSize) return INVALID_OPERATION;
+
String8 ashmemName("CursorWindow: ");
- ashmemName.append(name);
+ ashmemName.append(mName);
status_t result;
- int ashmemFd = ashmem_create_region(ashmemName.string(), size);
+ int ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize);
if (ashmemFd < 0) {
result = -errno;
ALOGE("CursorWindow: ashmem_create_region() failed: errno=%d.", errno);
@@ -55,7 +94,8 @@ status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** o
if (result < 0) {
ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d",errno);
} else {
- void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
+ void* data = ::mmap(NULL, mInflatedSize, PROT_READ | PROT_WRITE,
+ MAP_SHARED, ashmemFd, 0);
if (data == MAP_FAILED) {
result = -errno;
ALOGE("CursorWindow: mmap() failed: errno=%d.", errno);
@@ -64,33 +104,49 @@ status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** o
if (result < 0) {
ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d.", errno);
} else {
- CursorWindow* window = new CursorWindow(name, ashmemFd,
- data, size, false /*readOnly*/);
- result = window->clear();
- if (!result) {
- LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
- "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
- window->mHeader->freeOffset,
- window->mHeader->numRows,
- window->mHeader->numColumns,
- window->mSize, window->mData);
- *outCursorWindow = window;
- return OK;
- }
- delete window;
+ // Move inline contents into new ashmem region
+ memcpy(data, mData, mSize);
+ free(mData);
+ mAshmemFd = ashmemFd;
+ mData = data;
+ mHeader = static_cast<Header*>(mData);
+ mSize = mInflatedSize;
+ LOG_WINDOW("Inflated CursorWindow: freeOffset=%d, "
+ "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
+ mHeader->freeOffset,
+ mHeader->numRows,
+ mHeader->numColumns,
+ mSize, mData);
+ return OK;
}
}
- ::munmap(data, size);
+ ::munmap(data, mInflatedSize);
}
::close(ashmemFd);
}
- *outCursorWindow = NULL;
return result;
}
status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) {
- String8 name = parcel->readString8();
+ *outCursorWindow = nullptr;
+
+ String8 name;
+ status_t result = parcel->readString8(&name);
+ if (result) return result;
+
+ bool isAshmem;
+ result = parcel->readBool(&isAshmem);
+ if (result) return result;
+ if (isAshmem) {
+ return createFromParcelAshmem(parcel, name, outCursorWindow);
+ } else {
+ return createFromParcelInline(parcel, name, outCursorWindow);
+ }
+}
+
+status_t CursorWindow::createFromParcelAshmem(Parcel* parcel, String8& name,
+ CursorWindow** outCursorWindow) {
status_t result;
int actualSize;
int ashmemFd = parcel->readFileDescriptor();
@@ -122,8 +178,8 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursor
actualSize, (int) size, errno);
} else {
CursorWindow* window = new CursorWindow(name, dupAshmemFd,
- data, size, true /*readOnly*/);
- LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, "
+ data, size, size, true /*readOnly*/);
+ LOG_WINDOW("Created CursorWindow from ashmem parcel: freeOffset=%d, "
"numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
window->mHeader->freeOffset,
window->mHeader->numRows,
@@ -140,12 +196,62 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursor
return result;
}
+status_t CursorWindow::createFromParcelInline(Parcel* parcel, String8& name,
+ CursorWindow** outCursorWindow) {
+ uint32_t sentSize;
+ status_t result = parcel->readUint32(&sentSize);
+ if (result) return result;
+ if (sentSize > kInlineSize) return NO_MEMORY;
+
+ void* data = calloc(sentSize, 1);
+ if (!data) return NO_MEMORY;
+
+ result = parcel->read(data, sentSize);
+ if (result) return result;
+
+ CursorWindow* window = new CursorWindow(name, -1, data, sentSize,
+ sentSize, true /*readOnly*/);
+ LOG_WINDOW("Created CursorWindow from inline parcel: freeOffset=%d, "
+ "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
+ window->mHeader->freeOffset,
+ window->mHeader->numRows,
+ window->mHeader->numColumns,
+ window->mSize, window->mData);
+ *outCursorWindow = window;
+ return OK;
+}
+
status_t CursorWindow::writeToParcel(Parcel* parcel) {
- status_t status = parcel->writeString8(mName);
- if (!status) {
- status = parcel->writeDupFileDescriptor(mAshmemFd);
+ LOG_WINDOW("Writing CursorWindow: freeOffset=%d, "
+ "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
+ mHeader->freeOffset,
+ mHeader->numRows,
+ mHeader->numColumns,
+ mSize, mData);
+
+ status_t result = parcel->writeString8(mName);
+ if (result) return result;
+
+ if (mAshmemFd != -1) {
+ result = parcel->writeBool(true);
+ if (result) return result;
+ return writeToParcelAshmem(parcel);
+ } else {
+ result = parcel->writeBool(false);
+ if (result) return result;
+ return writeToParcelInline(parcel);
}
- return status;
+}
+
+status_t CursorWindow::writeToParcelAshmem(Parcel* parcel) {
+ return parcel->writeDupFileDescriptor(mAshmemFd);
+}
+
+status_t CursorWindow::writeToParcelInline(Parcel* parcel) {
+ status_t result = parcel->writeUint32(mHeader->freeOffset);
+ if (result) return result;
+
+ return parcel->write(mData, mHeader->freeOffset);
}
status_t CursorWindow::clear() {
@@ -187,6 +293,7 @@ status_t CursorWindow::allocRow() {
if (rowSlot == NULL) {
return NO_MEMORY;
}
+ uint32_t rowSlotOffset = offsetFromPtr(rowSlot);
// Allocate the slots for the field directory
size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot);
@@ -201,7 +308,8 @@ status_t CursorWindow::allocRow() {
memset(fieldDir, 0, fieldDirSize);
LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n",
- mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset);
+ mHeader->numRows - 1, rowSlotOffset, fieldDirSize, fieldDirOffset);
+ rowSlot = static_cast<RowSlot*>(offsetToPtr(rowSlotOffset));
rowSlot->offset = fieldDirOffset;
return OK;
}
@@ -229,10 +337,14 @@ uint32_t CursorWindow::alloc(size_t size, bool aligned) {
uint32_t offset = mHeader->freeOffset + padding;
uint32_t nextFreeOffset = offset + size;
if (nextFreeOffset > mSize) {
- ALOGW("Window is full: requested allocation %zu bytes, "
- "free space %zu bytes, window size %zu bytes",
- size, freeSpace(), mSize);
- return 0;
+ // Try inflating to ashmem before finally giving up
+ inflate();
+ if (nextFreeOffset > mSize) {
+ ALOGW("Window is full: requested allocation %zu bytes, "
+ "free space %zu bytes, window size %zu bytes",
+ size, freeSpace(), mSize);
+ return 0;
+ }
}
mHeader->freeOffset = nextFreeOffset;
@@ -260,7 +372,10 @@ CursorWindow::RowSlot* CursorWindow::allocRowSlot() {
}
if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) {
if (!chunk->nextChunkOffset) {
- chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/);
+ uint32_t chunkOffset = offsetFromPtr(chunk);
+ uint32_t newChunk = alloc(sizeof(RowSlotChunk), true /*aligned*/);
+ chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunkOffset));
+ chunk->nextChunkOffset = newChunk;
if (!chunk->nextChunkOffset) {
return NULL;
}
@@ -308,6 +423,7 @@ status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
if (!fieldSlot) {
return BAD_VALUE;
}
+ uint32_t fieldSlotOffset = offsetFromPtr(fieldSlot);
uint32_t offset = alloc(size);
if (!offset) {
@@ -316,6 +432,7 @@ status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
memcpy(offsetToPtr(offset), value, size);
+ fieldSlot = static_cast<FieldSlot*>(offsetToPtr(fieldSlotOffset));
fieldSlot->type = type;
fieldSlot->data.buffer.offset = offset;
fieldSlot->data.buffer.size = size;
diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h
index ad64b246b3f5..0bee60929cc9 100644
--- a/libs/androidfw/include/androidfw/CursorWindow.h
+++ b/libs/androidfw/include/androidfw/CursorWindow.h
@@ -50,8 +50,8 @@ namespace android {
* Strings are stored in UTF-8.
*/
class CursorWindow {
- CursorWindow(const String8& name, int ashmemFd,
- void* data, size_t size, bool readOnly);
+ CursorWindow(const String8& name, int ashmemFd, void* data, size_t size,
+ size_t inflatedSize, bool readOnly);
public:
/* Field types. */
@@ -165,11 +165,12 @@ private:
int mAshmemFd;
void* mData;
size_t mSize;
+ size_t mInflatedSize;
bool mReadOnly;
Header* mHeader;
inline void* offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) {
- if (offset >= mSize) {
+ if (offset > mSize) {
ALOGE("Offset %" PRIu32 " out of bounds, max value %zu", offset, mSize);
return NULL;
}
@@ -185,6 +186,18 @@ private:
return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
}
+ static status_t createFromParcelAshmem(Parcel*, String8&, CursorWindow**);
+ static status_t createFromParcelInline(Parcel*, String8&, CursorWindow**);
+
+ status_t writeToParcelAshmem(Parcel*);
+ status_t writeToParcelInline(Parcel*);
+
+ /**
+ * By default windows are lightweight inline allocations; this method
+ * inflates the window into a larger ashmem region.
+ */
+ status_t inflate();
+
/**
* Allocate a portion of the window. Returns the offset
* of the allocation, or 0 if there isn't enough space.
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index 523a072b957a..dbf4ad0b14f9 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -18,13 +18,13 @@ package android.media;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.StringDef;
-import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.browse.MediaBrowser;
import android.media.session.MediaController;
+import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
@@ -417,14 +417,17 @@ public final class MediaMetadata implements Parcelable {
}
private final Bundle mBundle;
+ private final int mBitmapDimensionLimit;
private MediaDescription mDescription;
- private MediaMetadata(Bundle bundle) {
+ private MediaMetadata(Bundle bundle, int bitmapDimensionLimit) {
mBundle = new Bundle(bundle);
+ mBitmapDimensionLimit = bitmapDimensionLimit;
}
private MediaMetadata(Parcel in) {
mBundle = in.readBundle();
+ mBitmapDimensionLimit = Math.max(in.readInt(), 0);
}
/**
@@ -513,6 +516,22 @@ public final class MediaMetadata implements Parcelable {
return bmp;
}
+ /**
+ * Gets the width/height limit (in pixels) for the bitmaps when this metadata was created.
+ * This method returns a positive value or zero.
+ * <p>
+ * If it returns a positive value, then all the bitmaps in this metadata has width/height
+ * not greater than this limit. Bitmaps may have been scaled down according to the limit.
+ * <p>
+ * If it returns zero, then no scaling down was applied to the bitmaps when this metadata
+ * was created.
+ *
+ * @see Builder#setBitmapDimensionLimit(int)
+ */
+ public @IntRange(from = 0) int getBitmapDimensionLimit() {
+ return mBitmapDimensionLimit;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -521,6 +540,7 @@ public final class MediaMetadata implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeBundle(mBundle);
+ dest.writeInt(mBitmapDimensionLimit);
}
/**
@@ -718,6 +738,7 @@ public final class MediaMetadata implements Parcelable {
*/
public static final class Builder {
private final Bundle mBundle;
+ private int mBitmapDimensionLimit;
/**
* Create an empty Builder. Any field that should be included in the
@@ -736,30 +757,7 @@ public final class MediaMetadata implements Parcelable {
*/
public Builder(MediaMetadata source) {
mBundle = new Bundle(source.mBundle);
- }
-
- /**
- * Create a Builder using a {@link MediaMetadata} instance to set
- * initial values, but replace bitmaps with a scaled down copy if their width (or height)
- * is larger than maxBitmapSize.
- *
- * @param source The original metadata to copy.
- * @param maxBitmapSize The maximum height/width for bitmaps contained
- * in the metadata.
- * @hide
- */
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public Builder(@NonNull MediaMetadata source, @IntRange(from = 1) int maxBitmapSize) {
- this(source);
- for (String key : mBundle.keySet()) {
- Object value = mBundle.get(key);
- if (value != null && value instanceof Bitmap) {
- Bitmap bmp = (Bitmap) value;
- if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
- putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
- }
- }
- }
+ mBitmapDimensionLimit = source.mBitmapDimensionLimit;
}
/**
@@ -902,9 +900,9 @@ public final class MediaMetadata implements Parcelable {
* <li>{@link #METADATA_KEY_DISPLAY_ICON}</li>
* </ul>
* <p>
- * Large bitmaps may be scaled down by the system when
- * {@link android.media.session.MediaSession#setMetadata} is called.
- * To pass full resolution images {@link Uri Uris} should be used with
+ * Large bitmaps may be scaled down by the system with
+ * {@link Builder#setBitmapDimensionLimit(int)} when {@link MediaSession#setMetadata}
+ * is called. To pass full resolution images {@link Uri Uris} should be used with
* {@link #putString}.
*
* @param key The key for referencing this value
@@ -923,18 +921,46 @@ public final class MediaMetadata implements Parcelable {
}
/**
+ * Sets the maximum width/height (in pixels) for the bitmaps in the metadata.
+ * Bitmaps will be replaced with scaled down copies if their width (or height) is
+ * larger than {@code bitmapDimensionLimit}.
+ * <p>
+ * In order to unset the limit, pass zero as {@code bitmapDimensionLimit}.
+ *
+ * @param bitmapDimensionLimit The maximum width/height (in pixels) for bitmaps
+ * contained in the metadata. Pass {@code 0} to unset the limit.
+ */
+ @NonNull
+ public Builder setBitmapDimensionLimit(int bitmapDimensionLimit) {
+ mBitmapDimensionLimit = Math.max(bitmapDimensionLimit, 0);
+ return this;
+ }
+
+ /**
* Creates a {@link MediaMetadata} instance with the specified fields.
*
* @return The new MediaMetadata instance
*/
public MediaMetadata build() {
- return new MediaMetadata(mBundle);
+ if (mBitmapDimensionLimit > 0) {
+ for (String key : mBundle.keySet()) {
+ Object value = mBundle.get(key);
+ if (value instanceof Bitmap) {
+ Bitmap bmp = (Bitmap) value;
+ if (bmp.getHeight() > mBitmapDimensionLimit
+ || bmp.getWidth() > mBitmapDimensionLimit) {
+ putBitmap(key, scaleBitmap(bmp, mBitmapDimensionLimit));
+ }
+ }
+ }
+ }
+ return new MediaMetadata(mBundle, mBitmapDimensionLimit);
}
- private Bitmap scaleBitmap(Bitmap bmp, int maxSize) {
- float maxSizeF = maxSize;
- float widthScale = maxSizeF / bmp.getWidth();
- float heightScale = maxSizeF / bmp.getHeight();
+ private Bitmap scaleBitmap(Bitmap bmp, int maxDimension) {
+ float maxDimensionF = maxDimension;
+ float widthScale = maxDimensionF / bmp.getWidth();
+ float heightScale = maxDimensionF / bmp.getHeight();
float scale = Math.min(widthScale, heightScale);
int height = (int) (bmp.getHeight() * scale);
int width = (int) (bmp.getWidth() * scale);
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index 21376bb0fecb..8cbe52f7c481 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -462,9 +462,7 @@ public final class MediaTranscodeManager {
mTranscodingClient = service.registerClient(
mTranscodingClientCallback,
mPackageName,
- mPackageName,
- IMediaTranscodingService.USE_CALLING_UID,
- IMediaTranscodingService.USE_CALLING_PID);
+ mPackageName);
if (mTranscodingClient != null) {
mTranscodingClient.asBinder().linkToDeath(() -> onClientDied(), /* flags */ 0);
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index d35bc4176cb3..d02b49697821 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -372,7 +372,7 @@ public class Ringtone {
AudioAttributes.toLegacyStreamType(mAudioAttributes)) != 0) {
startLocalPlayer();
}
- } else if (mAllowRemote && (mRemotePlayer != null)) {
+ } else if (mAllowRemote && (mRemotePlayer != null) && (mUri != null)) {
final Uri canonicalUri = mUri.getCanonicalUri();
final boolean looping;
final float volume;
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 8deb0c4451ea..c75296c73a90 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -1130,11 +1130,13 @@ public class RingtoneManager {
// Try finding the scanned ringtone
final String filename = getDefaultRingtoneFilename(type);
+ final String whichAudio = getQueryStringForType(type);
+ final String where = MediaColumns.DISPLAY_NAME + "=? AND " + whichAudio + "=?";
final Uri baseUri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
try (Cursor cursor = context.getContentResolver().query(baseUri,
new String[] { MediaColumns._ID },
- MediaColumns.DISPLAY_NAME + "=?",
- new String[] { filename }, null)) {
+ where,
+ new String[] { filename, "1" }, null)) {
if (cursor.moveToFirst()) {
final Uri ringtoneUri = context.getContentResolver().canonicalizeOrElse(
ContentUris.withAppendedId(baseUri, cursor.getLong(0)));
@@ -1162,4 +1164,13 @@ public class RingtoneManager {
default: throw new IllegalArgumentException();
}
}
+
+ private static String getQueryStringForType(int type) {
+ switch (type) {
+ case TYPE_RINGTONE: return MediaStore.Audio.AudioColumns.IS_RINGTONE;
+ case TYPE_NOTIFICATION: return MediaStore.Audio.AudioColumns.IS_NOTIFICATION;
+ case TYPE_ALARM: return MediaStore.Audio.AudioColumns.IS_ALARM;
+ default: throw new IllegalArgumentException();
+ }
+ }
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 624607b61b8f..e17e069d61d0 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -464,7 +464,9 @@ public final class MediaSession {
int fields = 0;
MediaDescription description = null;
if (metadata != null) {
- metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build();
+ metadata = new MediaMetadata.Builder(metadata)
+ .setBitmapDimensionLimit(mMaxBitmapSize)
+ .build();
if (metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
}
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 1fbb67260895..4380c134b82b 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -25,6 +25,7 @@ import android.media.tv.ITvInputClient;
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
import android.media.tv.ITvInputManagerCallback;
+import android.media.tv.TvChannelInfo;
import android.media.tv.TvContentRatingSystemInfo;
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
@@ -88,6 +89,8 @@ interface ITvInputManager {
void timeShiftSetPlaybackParams(in IBinder sessionToken, in PlaybackParams params, int userId);
void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);
+ List<TvChannelInfo> getCurrentTvChannelInfos(int userId);
+
// For the recording session
void startRecording(in IBinder sessionToken, in Uri programUri, in Bundle params, int userId);
void stopRecording(in IBinder sessionToken, int userId);
diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
index 0f8bf2fb3a1d..9f80bf52123a 100644
--- a/media/java/android/media/tv/ITvInputManagerCallback.aidl
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.media.tv.TvChannelInfo;
import android.media.tv.TvInputInfo;
/**
@@ -28,4 +29,5 @@ oneway interface ITvInputManagerCallback {
void onInputUpdated(in String inputId);
void onInputStateChanged(in String inputId, int state);
void onTvInputInfoUpdated(in TvInputInfo TvInputInfo);
+ void onCurrentTvChannelInfosUpdated(in List<TvChannelInfo> currentTvChannelInfos);
}
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/media/java/android/media/tv/TvChannelInfo.aidl
new file mode 100644
index 000000000000..71cd0a70c9be
--- /dev/null
+++ b/media/java/android/media/tv/TvChannelInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+parcelable TvChannelInfo;
diff --git a/media/java/android/media/tv/TvChannelInfo.java b/media/java/android/media/tv/TvChannelInfo.java
new file mode 100644
index 000000000000..635b13045921
--- /dev/null
+++ b/media/java/android/media/tv/TvChannelInfo.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @hide
+ */
+public final class TvChannelInfo implements Parcelable {
+ static final String TAG = "TvChannelInfo";
+ public static final int APP_TAG_SELF = 0;
+ public static final int APP_TYPE_SELF = 1;
+ public static final int APP_TYPE_SYSTEM = 2;
+ public static final int APP_TYPE_NON_SYSTEM = 3;
+
+ /** @hide */
+ @IntDef(prefix = "APP_TYPE_", value = {APP_TYPE_SELF, APP_TYPE_SYSTEM, APP_TYPE_NON_SYSTEM})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AppType {}
+
+ public static final @NonNull Parcelable.Creator<TvChannelInfo> CREATOR =
+ new Parcelable.Creator<TvChannelInfo>() {
+ @Override
+ public TvChannelInfo createFromParcel(Parcel source) {
+ try {
+ return new TvChannelInfo(source);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating TvChannelInfo from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public TvChannelInfo[] newArray(int size) {
+ return new TvChannelInfo[size];
+ }
+ };
+
+
+ private final String mInputId;
+ @Nullable private final Uri mChannelUri;
+ private final boolean mIsRecordingSession;
+ private final boolean mIsForeground;
+ @AppType private final int mAppType;
+ private final int mAppTag;
+
+ public TvChannelInfo(
+ String inputId, @Nullable Uri channelUri, boolean isRecordingSession,
+ boolean isForeground, @AppType int appType, int appTag) {
+ mInputId = inputId;
+ mChannelUri = channelUri;
+ mIsRecordingSession = isRecordingSession;
+ mIsForeground = isForeground;
+ mAppType = appType;
+ mAppTag = appTag;
+ }
+
+
+ private TvChannelInfo(Parcel source) {
+ mInputId = source.readString();
+ String uriString = source.readString();
+ mChannelUri = uriString == null ? null : Uri.parse(uriString);
+ mIsRecordingSession = (source.readInt() == 1);
+ mIsForeground = (source.readInt() == 1);
+ mAppType = source.readInt();
+ mAppTag = source.readInt();
+ }
+
+ public String getInputId() {
+ return mInputId;
+ }
+
+ public Uri getChannelUri() {
+ return mChannelUri;
+ }
+
+ public boolean isRecordingSession() {
+ return mIsRecordingSession;
+ }
+
+ public boolean isForeground() {
+ return mIsForeground;
+ }
+
+ /**
+ * Gets app tag.
+ * <p>App tag is used to differentiate one app from another.
+ * {@link #APP_TAG_SELF} is for current app.
+ */
+ public int getAppTag() {
+ return mAppTag;
+ }
+
+ @AppType
+ public int getAppType() {
+ return mAppType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mInputId);
+ String uriString = mChannelUri == null ? null : mChannelUri.toString();
+ dest.writeString(uriString);
+ dest.writeInt(mIsRecordingSession ? 1 : 0);
+ dest.writeInt(mIsForeground ? 1 : 0);
+ dest.writeInt(mAppType);
+ dest.writeInt(mAppTag);
+ }
+
+ @Override
+ public String toString() {
+ return "inputID=" + mInputId
+ + ";channelUri=" + mChannelUri
+ + ";isRecording=" + mIsRecordingSession
+ + ";isForeground=" + mIsForeground
+ + ";appType=" + mAppType
+ + ";appTag=" + mAppTag;
+ }
+}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 98a01a4cb449..d38369fea2b8 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -899,6 +899,10 @@ public final class TvInputManager {
*/
public void onTvInputInfoUpdated(TvInputInfo inputInfo) {
}
+
+ /** @hide */
+ public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> tvChannelInfos) {
+ }
}
private static final class TvInputCallbackRecord {
@@ -958,6 +962,16 @@ public final class TvInputManager {
}
});
}
+
+ public void postCurrentTvChannelInfosUpdated(
+ final List<TvChannelInfo> currentTvChannelInfos) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onCurrentTvChannelInfosUpdated(currentTvChannelInfos);
+ }
+ });
+ }
}
/**
@@ -1262,6 +1276,15 @@ public final class TvInputManager {
}
}
}
+
+ @Override
+ public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> currentTvChannelInfos) {
+ synchronized (mLock) {
+ for (TvInputCallbackRecord record : mCallbackRecords) {
+ record.postCurrentTvChannelInfosUpdated(currentTvChannelInfos);
+ }
+ }
+ }
};
try {
if (mService != null) {
@@ -1953,6 +1976,17 @@ public final class TvInputManager {
}
/**
+ * @hide
+ */
+ public List<TvChannelInfo> getCurrentTvChannelInfos() {
+ try {
+ return mService.getCurrentTvChannelInfos(mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* The Session provides the per-session functionality of TV inputs.
* @hide
*/
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index d46f1d1606ba..ff954690501c 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -6138,6 +6138,7 @@ package android.app {
field @NonNull public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR;
field public static final int FLAG_CANCEL_CURRENT = 268435456; // 0x10000000
field public static final int FLAG_IMMUTABLE = 67108864; // 0x4000000
+ field public static final int FLAG_MUTABLE = 33554432; // 0x2000000
field public static final int FLAG_NO_CREATE = 536870912; // 0x20000000
field public static final int FLAG_ONE_SHOT = 1073741824; // 0x40000000
field public static final int FLAG_UPDATE_CURRENT = 134217728; // 0x8000000
@@ -8783,6 +8784,7 @@ package android.bluetooth {
method public void onPhyUpdate(android.bluetooth.BluetoothGatt, int, int, int);
method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
+ method public void onServiceChanged(@NonNull android.bluetooth.BluetoothGatt);
method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
}
@@ -26316,6 +26318,7 @@ package android.media {
method public boolean containsKey(String);
method public int describeContents();
method public android.graphics.Bitmap getBitmap(String);
+ method @IntRange(from=0) public int getBitmapDimensionLimit();
method @NonNull public android.media.MediaDescription getDescription();
method public long getLong(String);
method public android.media.Rating getRating(String);
@@ -26365,6 +26368,7 @@ package android.media {
method public android.media.MediaMetadata.Builder putRating(String, android.media.Rating);
method public android.media.MediaMetadata.Builder putString(String, String);
method public android.media.MediaMetadata.Builder putText(String, CharSequence);
+ method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(int);
}
@Deprecated public abstract class MediaMetadataEditor {
@@ -44233,6 +44237,7 @@ package android.telecom {
field public static final int MISSED = 5; // 0x5
field public static final int OTHER = 9; // 0x9
field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
+ field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
field public static final int REJECTED = 6; // 0x6
@@ -44932,6 +44937,7 @@ package android.telephony {
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
+ field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool";
@@ -45126,6 +45132,10 @@ package android.telephony {
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
field public static final int SERVICE_CLASS_NONE = 0; // 0x0
field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
+ field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
+ field public static final int USSD_OVER_CS_PREFERRED = 0; // 0x0
+ field public static final int USSD_OVER_IMS_ONLY = 3; // 0x3
+ field public static final int USSD_OVER_IMS_PREFERRED = 1; // 0x1
}
public static final class CarrierConfigManager.Apn {
@@ -51772,6 +51782,33 @@ package android.view {
field public int toolType;
}
+ public interface OnReceiveContentCallback<T extends android.view.View> {
+ method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull T);
+ method public boolean onReceiveContent(@NonNull T, @NonNull android.view.OnReceiveContentCallback.Payload);
+ }
+
+ public static final class OnReceiveContentCallback.Payload {
+ method @NonNull public android.content.ClipData getClip();
+ method @Nullable public android.os.Bundle getExtras();
+ method public int getFlags();
+ method @Nullable public android.net.Uri getLinkUri();
+ method public int getSource();
+ field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+ field public static final int SOURCE_AUTOFILL = 3; // 0x3
+ field public static final int SOURCE_CLIPBOARD = 0; // 0x0
+ field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2
+ field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
+ field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4
+ }
+
+ public static final class OnReceiveContentCallback.Payload.Builder {
+ ctor public OnReceiveContentCallback.Payload.Builder(@NonNull android.content.ClipData, int);
+ method @NonNull public android.view.OnReceiveContentCallback.Payload build();
+ method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setFlags(int);
+ method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setLinkUri(@Nullable android.net.Uri);
+ }
+
public abstract class OrientationEventListener {
ctor public OrientationEventListener(android.content.Context);
ctor public OrientationEventListener(android.content.Context, int);
@@ -52323,6 +52360,7 @@ package android.view {
method @IdRes public int getNextFocusRightId();
method @IdRes public int getNextFocusUpId();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
+ method @Nullable public android.view.OnReceiveContentCallback<? extends android.view.View> getOnReceiveContentCallback();
method @ColorInt public int getOutlineAmbientShadowColor();
method public android.view.ViewOutlineProvider getOutlineProvider();
method @ColorInt public int getOutlineSpotShadowColor();
@@ -52674,6 +52712,7 @@ package android.view {
method public void setOnHoverListener(android.view.View.OnHoverListener);
method public void setOnKeyListener(android.view.View.OnKeyListener);
method public void setOnLongClickListener(@Nullable android.view.View.OnLongClickListener);
+ method public void setOnReceiveContentCallback(@Nullable android.view.OnReceiveContentCallback<? extends android.view.View>);
method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
method @Deprecated public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
@@ -58936,17 +58975,6 @@ package android.widget {
method public android.view.View newGroupView(android.content.Context, android.database.Cursor, boolean, android.view.ViewGroup);
}
- public interface RichContentReceiver<T extends android.view.View> {
- method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes();
- method public boolean onReceive(@NonNull T, @NonNull android.content.ClipData, int, int);
- field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
- field public static final int SOURCE_AUTOFILL = 3; // 0x3
- field public static final int SOURCE_CLIPBOARD = 0; // 0x0
- field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2
- field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
- field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4
- }
-
public class ScrollView extends android.widget.FrameLayout {
ctor public ScrollView(android.content.Context);
ctor public ScrollView(android.content.Context, android.util.AttributeSet);
@@ -59501,10 +59529,10 @@ package android.widget {
method public int getMinWidth();
method public final android.text.method.MovementMethod getMovementMethod();
method public int getOffsetForPosition(float, float);
+ method @Nullable public android.view.OnReceiveContentCallback<android.widget.TextView> getOnReceiveContentCallback();
method public android.text.TextPaint getPaint();
method public int getPaintFlags();
method public String getPrivateImeOptions();
- method @NonNull public android.widget.RichContentReceiver<android.widget.TextView> getRichContentReceiver();
method public int getSelectionEnd();
method public int getSelectionStart();
method @ColorInt public int getShadowColor();
@@ -59632,7 +59660,6 @@ package android.widget {
method public void setPaintFlags(int);
method public void setPrivateImeOptions(String);
method public void setRawInputType(int);
- method public void setRichContentReceiver(@NonNull android.widget.RichContentReceiver<android.widget.TextView>);
method public void setScroller(android.widget.Scroller);
method public void setSelectAllOnFocus(boolean);
method public void setShadowLayer(float, float, float, int);
@@ -59673,7 +59700,6 @@ package android.widget {
method public void setWidth(int);
field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
- field @NonNull public static final android.widget.RichContentReceiver<android.widget.TextView> DEFAULT_RICH_CONTENT_RECEIVER;
}
public enum TextView.BufferType {
@@ -59690,6 +59716,12 @@ package android.widget {
field @NonNull public static final android.os.Parcelable.Creator<android.widget.TextView.SavedState> CREATOR;
}
+ public class TextViewOnReceiveContentCallback implements android.view.OnReceiveContentCallback<android.widget.TextView> {
+ ctor public TextViewOnReceiveContentCallback();
+ method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull android.widget.TextView);
+ method public boolean onReceiveContent(@NonNull android.widget.TextView, @NonNull android.view.OnReceiveContentCallback.Payload);
+ }
+
public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter {
method @Nullable public android.content.res.Resources.Theme getDropDownViewTheme();
method public void setDropDownViewTheme(@Nullable android.content.res.Resources.Theme);
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 6c0dd3394cd7..11cd6a93910e 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -16,6 +16,14 @@ package android.app {
}
+package android.app.role {
+
+ public final class RoleManager {
+ method @Nullable public String getDefaultSmsPackage(int);
+ }
+
+}
+
package android.content.rollback {
public class RollbackManagerFrameworkInitializer {
@@ -45,10 +53,6 @@ package android.media {
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
- public static final class MediaMetadata.Builder {
- ctor public MediaMetadata.Builder(@NonNull android.media.MediaMetadata, @IntRange(from=1) int);
- }
-
}
package android.media.session {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index e27ca09f8e86..916e21f43f61 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -9303,10 +9303,6 @@ package android.telecom {
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
}
- public final class DisconnectCause implements android.os.Parcelable {
- field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
- }
-
public abstract class InCallService extends android.app.Service {
method @Deprecated public android.telecom.Phone getPhone();
method @Deprecated public void onPhoneCreated(android.telecom.Phone);
@@ -10514,11 +10510,11 @@ package android.telephony.data {
method public int getSuggestedRetryTime();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
- field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 2; // 0x2
- field public static final int HANDOVER_FAILURE_MODE_LEGACY = 1; // 0x1
- field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 3; // 0x3
- field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 4; // 0x4
- field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = 0; // 0x0
+ field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; // 0x1
+ field public static final int HANDOVER_FAILURE_MODE_LEGACY = 0; // 0x0
+ field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 2; // 0x2
+ field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 3; // 0x3
+ field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = -1; // 0xffffffff
field public static final int LINK_STATUS_ACTIVE = 2; // 0x2
field public static final int LINK_STATUS_DORMANT = 1; // 0x1
field public static final int LINK_STATUS_INACTIVE = 0; // 0x0
diff --git a/packages/CarSystemUI/res/drawable/car_ic_settings_icon.xml b/packages/CarSystemUI/res/drawable/car_ic_settings_icon.xml
new file mode 100644
index 000000000000..147f5397361e
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_settings_icon.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@*android:dimen/status_bar_system_icon_size"
+ android:height="@*android:dimen/status_bar_system_icon_size"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@color/system_bar_icon_color"
+ android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4C9.75,2 9.54,2.18 9.51,2.42L9.13,5.07C8.52,5.32 7.96,5.66 7.44,6.05l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46C2.21,8.95 2.27,9.22 2.46,9.37l2.11,1.65C4.53,11.34 4.5,11.67 4.5,12s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65C9.54,21.82 9.75,22 10,22h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64L19.43,12.98zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5s3.5,1.57 3.5,3.5S13.93,15.5 12,15.5z"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml b/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml
index 1195d05da228..270d932714f3 100644
--- a/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml
@@ -21,5 +21,5 @@
android:viewportHeight="24">
<path
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"
- android:fillColor="@color/system_bar_user_icon_color"/>
+ android:fillColor="@color/system_bar_icon_color"/>
</vector> \ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index 07c11c76a4f2..7994b19242cd 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -36,6 +36,7 @@
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_marginTop="@dimen/car_padding_2"
+ android:layout_marginStart="@dimen/car_padding_2"
android:layout_centerVertical="true"
android:gravity="center_vertical"
>
@@ -44,7 +45,6 @@
android:layout_height="match_parent"
android:background="@drawable/system_bar_background_pill"
android:layout_weight="1"
- android:layout_marginStart="@dimen/car_padding_2"
android:gravity="center_vertical"
systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$QuickSettingActivity;launchFlags=0x24000000;end">
@@ -53,6 +53,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:layout_marginStart="@dimen/car_padding_2"
+ android:layout_marginEnd="@dimen/car_padding_2"
android:gravity="center_vertical"
/>
</com.android.systemui.car.navigationbar.CarNavigationButton>
diff --git a/packages/CarSystemUI/res/layout/system_icons.xml b/packages/CarSystemUI/res/layout/system_icons.xml
index 5c06075e8617..f6ffcc8c16f0 100644
--- a/packages/CarSystemUI/res/layout/system_icons.xml
+++ b/packages/CarSystemUI/res/layout/system_icons.xml
@@ -20,16 +20,24 @@
android:id="@+id/system_icons"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="center_vertical">
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
- android:padding="10dp"
android:scaleType="fitCenter"
android:gravity="center_vertical"
android:orientation="horizontal"
/>
+
+ <ImageView
+ android:id="@+id/settingsIcon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/car_padding_2"
+ android:src="@drawable/car_ic_settings_icon"
+ />
</LinearLayout> \ No newline at end of file
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index 6fe5004c459f..0181b9a39aea 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -33,7 +33,7 @@
<!-- colors for status bar -->
<color name="system_bar_background_pill_color">#282A2D</color>
- <color name="system_bar_user_icon_color">#FFFFFF</color>
+ <color name="system_bar_icon_color">#FFFFFF</color>
<color name="system_bar_text_color">#FFFFFF</color>
<color name="status_bar_background_color">#33000000</color>
<drawable name="system_bar_background">@color/status_bar_background_color</drawable>
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index fe060ac8e707..86bfa751d085 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -46,6 +46,10 @@
in frameworks/base/core package and thus will have no effect if
set here. See car_product overlay for car specific defaults-->
+ <!-- Overrides the space between each status icon in the system bar -->
+ <dimen name="status_bar_system_icon_spacing">16dp</dimen>
+ <!-- Overrides the size of the network signal icon -->
+ <dimen name="signal_icon_size">32dp</dimen>
<dimen name="system_bar_user_icon_padding">16dp</dimen>
<dimen name="system_bar_user_icon_drawing_size">36dp</dimen>
<!-- Padding on either side of the group of all system bar buttons -->
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
index b113d29f00e6..f2ca4956be07 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
@@ -50,6 +50,7 @@ public class DisplaySystemBarsController extends DisplayImeController {
private final Context mContext;
private final DisplayController mDisplayController;
+ private final Handler mHandler;
private SparseArray<PerDisplay> mPerDisplaySparseArray;
public DisplaySystemBarsController(
@@ -58,9 +59,10 @@ public class DisplaySystemBarsController extends DisplayImeController {
DisplayController displayController,
@Main Handler mainHandler,
TransactionPool transactionPool) {
- super(wmService, displayController, mainHandler, transactionPool);
+ super(wmService, displayController, (r) -> mainHandler.post(r), transactionPool);
mContext = context;
mDisplayController = displayController;
+ mHandler = mainHandler;
}
@Override
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index 3b00576afd89..bf508b2ad7d1 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"الألمانية السويسرية"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"البلجيكية"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"البلغارية"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"بلغارية صوتية"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"الإيطالية"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"الدانماركية"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"النرويجية"</string>
diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml
index 19292780875e..f387414f50c8 100644
--- a/packages/InputDevices/res/values-bn/strings.xml
+++ b/packages/InputDevices/res/values-bn/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"সুইস জার্মান"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"বেলজিয়ান"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"বুলগেরীয়"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"বুলগেরিয়ান, ফনেটিক"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ইতালীয়"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ডেনিশ"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"নরওয়েজীয়"</string>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index 99aed5e6ea0e..6b4f7ebf7a4a 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švýcarské (němčina)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgické"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bulharské"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulharská fonetická klávesnice"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italské"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"dánské"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norské"</string>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 697fff6f1339..45aca35eb012 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Allemand (Suisse)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belge"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgare"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Phonétique bulgare"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italien"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danois"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvégien"</string>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index 2e14019340fc..b55a3c99f410 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Allemand (Suisse)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belge"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgare"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Phonétique bulgare"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italien"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danois"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvégien"</string>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 2ffebdd2c1ff..7d1e2f8106e7 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"स्विस जर्मन"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"बेल्जियाई"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"बुल्‍गारियाई"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"बुल्गेरियन, फ़ोनेटिक"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"इटैलियन"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"डैनिश"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"नार्वेजियाई"</string>
diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml
index cfdc3f85de4a..dfe8c56802c7 100644
--- a/packages/InputDevices/res/values-kk/strings.xml
+++ b/packages/InputDevices/res/values-kk/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Швейцариялық неміс"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Бельгиялық"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Болгар"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгар (фонетикалық)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Италиян"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Дат"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвег"</string>
diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index 2aaf816c7deb..3bd7f202f287 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"អាល្លឺម៉ង់ ស្វីស"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"បែលហ្ស៊ិក"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ប៊ុលហ្ការី"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ប៊ុលហ្គារី សូរសព្ទ"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"អ៊ីតាលី"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ដាណឺម៉ាក"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ន័រវែស"</string>
diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index c036409c9c62..9584e156ad9c 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Германски (Швајцарија)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Белгиски"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Бугарски"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Бугарска, фонетска"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Италијански"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Дански"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвешки"</string>
diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml
index 24816c1126e3..4801f752fa6c 100644
--- a/packages/InputDevices/res/values-ne/strings.xml
+++ b/packages/InputDevices/res/values-ne/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"स्विस-जर्मन"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"बेल्जियन"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"बुल्गेरियन"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"बुल्गेरियन फोनेटिक किबोर्ड"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"इटालियन"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"डेनिश"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"नर्वेजियन"</string>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 0cb4f34d4ab1..70668133ae8b 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -27,7 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"финский"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"хорватский"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"чешский"</string>
- <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чешский (QWERTY)"</string>
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"чешский (QWERTY)"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"эстонский"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"венгерский"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"исландский"</string>
@@ -44,9 +44,9 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"испанский (Латинская Америка)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"латышский"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Персидский"</string>
- <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Азербайджанский"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"азербайджанский"</string>
<string name="keyboard_layout_polish" msgid="1121588624094925325">"польский"</string>
- <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Белорусский"</string>
- <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Монгольский"</string>
- <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузинский"</string>
+ <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"белорусский"</string>
+ <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"монгольский"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузинский"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index b3677cd699b4..9b4fe9ee7580 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"gjermanishte zvicerane"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belge"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bullgarisht"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Tastierë bullgare, fonetike"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italisht"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"danisht"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norvegjisht"</string>
diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml
index b75b57d5f358..d3c6000fd9d4 100644
--- a/packages/InputDevices/res/values-ta/strings.xml
+++ b/packages/InputDevices/res/values-ta/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ஸ்விஸ் ஜெர்மன்"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"பெல்ஜியன்"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"பல்கேரியன்"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"பல்கேரியன், ஒலிப்புமுறை"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"இத்தாலியன்"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"டேனிஷ்"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"நார்வேஜியன்"</string>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index 21ea5de903bd..b9aee76d400c 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Swiss German"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgian"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarian"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarian, Phonetic"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italian"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danish"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegian"</string>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index 1b42ece3deed..76fd0bff0e2a 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -19,7 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Tiếng Đức Thụy Sĩ"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Tiếng Bỉ"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Tiếng Bungary"</string>
- <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Tiếng Bulgaria, Ngữ âm"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Tiếng Bulgaria, Phiên âm"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Tiếng Ý"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Tiếng Đan Mạch"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Tiếng Na Uy"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 696ed2955daa..f13d9b860dce 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -538,7 +538,7 @@
<string name="user_setup_button_setup_now" msgid="1708269547187760639">"지금 설정"</string>
<string name="user_setup_button_setup_later" msgid="8712980133555493516">"나중에"</string>
<string name="user_add_user_type_title" msgid="551279664052914497">"추가"</string>
- <string name="user_new_user_name" msgid="60979820612818840">"새 사용자"</string>
+ <string name="user_new_user_name" msgid="60979820612818840">"신규 사용자"</string>
<string name="user_new_profile_name" msgid="2405500423304678841">"새 프로필"</string>
<string name="user_info_settings_title" msgid="6351390762733279907">"사용자 정보"</string>
<string name="profile_info_settings_title" msgid="105699672534365099">"프로필 정보"</string>
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9788b30f82c4..08109798841f 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -437,6 +437,7 @@ public class SettingsBackupTest {
Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG,
Settings.Global.SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED,
Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
+ Settings.Global.SHOW_PEOPLE_SPACE,
Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
Settings.Global.SHOW_TEMPERATURE_WARNING,
Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index ce427cbe776b..0eac4add7b09 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -511,14 +511,14 @@ public class BugreportProgressService extends Service {
}
if (msg.what != MSG_SERVICE_COMMAND) {
- // Sanity check.
+ // Confidence check.
Log.e(TAG, "Invalid message type: " + msg.what);
return;
}
// At this point it's handling onStartCommand(), with the intent passed as an Extra.
if (!(msg.obj instanceof Intent)) {
- // Sanity check.
+ // Confidence check.
Log.wtf(TAG, "handleMessage(): invalid msg.obj type: " + msg.obj);
return;
}
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index cd3cad1d6351..3b02e3b46557 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -466,7 +466,7 @@ public class BugreportReceiverTest {
// Clear properties
mContext.getSharedPreferences(PREFS_BUGREPORT, Context.MODE_PRIVATE)
.edit().clear().commit();
- // Sanity check...
+ // Confidence check...
assertEquals("Did not reset properties", STATE_UNKNOWN,
getWarningState(mContext, STATE_UNKNOWN));
} else {
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index e1d195028dcb..b09e5b9b30a3 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -899,9 +899,9 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"إظهار رموز الإشعارات ذات الأولوية المنخفضة"</string>
<string name="other" msgid="429768510980739978">"غير ذلك"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"إزالة البطاقة"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"إضافة بطاقة إلى النهاية"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"نقل البطاقة"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"إزالة بطاقة"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"إضافة بطاقة إلى نهاية الإعدادات السريعة"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"نقل بطاقة"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"إضافة بطاقة"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"الانتقال إلى <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"الإضافة إلى الموضع <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index fcdd286b6f55..aed9522ced21 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"কম-গুরুত্বপূর্ণ বিজ্ঞপ্তির আইকন দেখুন"</string>
<string name="other" msgid="429768510980739978">"অন্যান্য"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল সরান"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"শেষে টাইল যোগ করুন"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল সরান"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"টাইল যোগ করুন"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>-এ সরান"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>-এ যোগ করুন"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"দ্রুত সেটিংস সম্পাদক৷"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> বিজ্ঞপ্তি: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"সেটিংস খুলুন।"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 783f7877e230..0a0368042d5f 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -880,9 +880,9 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostra les icones de notificació amb prioritat baixa"</string>
<string name="other" msgid="429768510980739978">"Altres"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"suprimir el mosaic"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"afegir una targeta al final"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mou la targeta"</string>
- <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Afegeix una targeta"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"afegir un mosaic al final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mou el mosaic"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Afegeix un mosaic"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mou a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Afegeix a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 3f3d6be63f9c..419e62510d50 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -889,20 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Zobrazit ikony oznámení s nízkou prioritou"</string>
<string name="other" msgid="429768510980739978">"Jiné"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranit dlaždici"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"přidat dlaždici na konec"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Přesunout dlaždici"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Přidat dlaždici"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Přesunout na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Přidat dlaždici na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rychlého nastavení"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Oznámení aplikace <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otevřít nastavení."</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 887aad5d9aa6..4dbd74ae4f48 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -28,15 +28,15 @@
<string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend"</string>
<string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"Noch <xliff:g id="PERCENTAGE">%1$s</xliff:g> übrig; bei deinem Nutzungsmuster hast du noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> ausstehend; noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Energiesparmodus ist aktiviert."</string>
+ <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Stromsparmodus ist aktiviert."</string>
<string name="invalid_charger" msgid="4370074072117767416">"Aufladen über USB nicht möglich. Verwende das mit dem Gerät gelieferte Ladegerät."</string>
<string name="invalid_charger_title" msgid="938685362320735167">"Aufladen über USB nicht möglich"</string>
<string name="invalid_charger_text" msgid="2339310107232691577">"Verwende das mit dem Gerät gelieferte Ladegerät"</string>
<string name="battery_low_why" msgid="2056750982959359863">"Einstellungen"</string>
- <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Energiesparmodus aktivieren?"</string>
- <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Energiesparmodus"</string>
+ <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Stromsparmodus aktivieren?"</string>
+ <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Stromsparmodus"</string>
<string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Aktivieren"</string>
- <string name="battery_saver_start_action" msgid="4553256017945469937">"Energiesparmodus aktivieren"</string>
+ <string name="battery_saver_start_action" msgid="4553256017945469937">"Stromsparmodus aktivieren"</string>
<string name="status_bar_settings_settings_button" msgid="534331565185171556">"Einstellungen"</string>
<string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"WLAN"</string>
<string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Bildschirm automatisch drehen"</string>
@@ -419,7 +419,7 @@
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"Bis <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Dunkles Design"</string>
- <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Energiesparmodus"</string>
+ <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Stromsparmodus"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"An bei Sonnenuntergang"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Bis Sonnenaufgang"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -497,9 +497,9 @@
<string name="user_remove_user_title" msgid="9124124694835811874">"Nutzer entfernen?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Alle Apps und Daten dieses Nutzers werden gelöscht."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Entfernen"</string>
- <string name="battery_saver_notification_title" msgid="8419266546034372562">"Energiesparmodus ist aktiviert"</string>
+ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Stromsparmodus ist aktiviert"</string>
<string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduzierung der Leistung und Hintergrunddaten"</string>
- <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Energiesparmodus deaktivieren"</string>
+ <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Stromsparmodus deaktivieren"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"Die App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise angezeigte Passwörter und Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Der Anbieter dieser App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Aufnahme oder Stream starten?"</string>
@@ -773,8 +773,8 @@
<item quantity="one">%d Minute</item>
</plurals>
<string name="battery_panel_title" msgid="5931157246673665963">"Akkunutzung"</string>
- <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Energiesparmodus ist beim Aufladen nicht verfügbar."</string>
- <string name="battery_detail_switch_title" msgid="6940976502957380405">"Energiesparmodus"</string>
+ <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Stromsparmodus ist beim Aufladen nicht verfügbar."</string>
+ <string name="battery_detail_switch_title" msgid="6940976502957380405">"Stromsparmodus"</string>
<string name="battery_detail_switch_summary" msgid="3668748557848025990">"Reduzierung der Leistung und Hintergrunddaten"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string>
@@ -961,11 +961,11 @@
<string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g> darf Teile aus jeder beliebigen App anzeigen"</string>
<string name="slice_permission_allow" msgid="6340449521277951123">"Zulassen"</string>
<string name="slice_permission_deny" msgid="6870256451658176895">"Ablehnen"</string>
- <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Energiesparmodus"</string>
+ <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Stromsparmodus"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Aktivieren, wenn der Akku wahrscheinlich nicht mehr lange hält"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nein danke"</string>
- <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Energiesparmodus aktiviert"</string>
- <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Energiesparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
+ <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Stromsparmodus aktiviert"</string>
+ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Stromsparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"Einstellungen"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"Ok"</string>
<string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index dfd42c3ea54f..8a8f1e737741 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -246,7 +246,7 @@
<string name="accessibility_remove_notification" msgid="1641455251495815527">"Eliminar notificación"</string>
<string name="accessibility_gps_enabled" msgid="4061313248217660858">"GPS habilitado"</string>
<string name="accessibility_gps_acquiring" msgid="896207402196024040">"Adquisición de GPS"</string>
- <string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter habilitado"</string>
+ <string name="accessibility_tty_enabled" msgid="1123180388823381118">"Teletipo habilitado"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Timbre en vibración"</string>
<string name="accessibility_ringer_silent" msgid="8994620163934249882">"Timbre en silencio"</string>
<!-- no translation found for accessibility_casting (8708751252897282313) -->
@@ -879,8 +879,8 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar íconos de notificaciones con prioridad baja"</string>
<string name="other" msgid="429768510980739978">"Otros"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Quitar tarjeta"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"Agregar tarjeta al final"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar tarjeta"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"agregar tarjeta al final"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover la tarjeta"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Agregar tarjeta"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index c1ab20c9ebb6..e8d2f4763348 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -881,11 +881,11 @@
<string name="other" msgid="429768510980739978">"Beste bat"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"kendu lauza"</string>
<string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"gehitu lauza amaieran"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Aldatu tokiz lauza"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mugitu lauza"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Gehitu lauza"</string>
- <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren kokapenera"</string>
- <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren kokapenean"</string>
- <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Kokapena: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren lekura"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren lekuan"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>garren lekua"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ezarpen bizkorren editorea."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> zerbitzuaren jakinarazpena: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ireki ezarpenak."</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index ea6de66db50f..ff89ce2dabcd 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification de faible priorité"</string>
<string name="other" msgid="429768510980739978">"Autre"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"retirer la tuile"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ajouter la tuile à la fin"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer la tuile"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ajouter la tuile"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de paramètres rapides."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index fc2691e3570d..a126cfaadd65 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification à faible priorité"</string>
<string name="other" msgid="429768510980739978">"Autre"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"supprimer la carte"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ajouter la carte à la fin"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer la carte"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ajouter une carte"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de configuration rapide."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 1a69e81a8191..9a563d960cf3 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -881,20 +881,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकता वाली सूचना के आइकॉन दिखाएं"</string>
<string name="other" msgid="429768510980739978">"अन्य"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाएं"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"टाइल को आखिरी पोज़िशन पर जोड़ें"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल को किसी और पोज़िशन पर ले जाएं"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"टाइल जोड़ें"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर ले जाएं"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर जोड़ें"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"टाइल की पोज़िशन <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"त्वरित सेटिंग संपादक."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिंग खोलें."</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 28c79fbc8852..171773c95aed 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -880,7 +880,7 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Ցուցադրել ցածր առաջնահերթության ծանուցումների պատկերակները"</string>
<string name="other" msgid="429768510980739978">"Այլ"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"հեռացնել սալիկը"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ավելացնել սալիկ վերջում"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ավելացնել սալիկը վերջում"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Տեղափոխել սալիկը"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ավելացնել սալիկ"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Տեղափոխել դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 1f8f2aa2fc27..4a750a594dad 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -879,10 +879,10 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Sýna tákn fyrir tilkynningar með litlum forgangi"</string>
<string name="other" msgid="429768510980739978">"Annað"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjarlægja reit"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"bæta reit við aftast"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Færa reit"</string>
- <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Bæta reit við"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjarlægja flís"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"bæta flís við aftast"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Færa flís"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Bæta flís við"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Færa í <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bæta við í stöðu <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Staða <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 31e6befca5ff..e6fbdb54a9de 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -890,10 +890,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"הצגת סמלי התראות בעדיפות נמוכה"</string>
<string name="other" msgid="429768510980739978">"אחר"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"הסרת האריח"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"הוספת האריח לקצה"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"הזזת האריח"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"הוספת האריח בסוף הרשימה"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"העברת האריח"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"הוספת אריח"</string>
- <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"העברה אל <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"העברה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"הוספה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"עורך הגדרות מהירות."</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index d775823b298c..b1fab0781b2a 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -879,10 +879,10 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Анча маанилүү эмес билдирменин сүрөтчөлөрүн көрсөтүү"</string>
<string name="other" msgid="429768510980739978">"Башка"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"плитканы өчүрүү"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"плитканы аягына кошуу"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Плитканы жылдыруу"</string>
- <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Плитка кошуу"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ыкчам баскычты өчүрүү"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ыкчам баскычты аягына кошуу"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Ыкчам баскычты жылдыруу"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ыкчам баскыч кошуу"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Төмөнкүгө жылдыруу: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>-позицияга кошуу"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>-позиция"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index fc6868152dc0..80f0106bbd20 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -883,8 +883,8 @@
<string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ടൈൽ, അവസാന ഭാഗത്ത് ചേർക്കുക"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ടൈൽ നീക്കുക"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ടൈൽ ചേർക്കുക"</string>
- <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>-ലേക്ക് നീക്കുക"</string>
- <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>-ൽ ചേർക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> എന്നതിലേക്ക് നീക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> എന്ന സ്ഥാനത്തേക്ക് ചേർക്കുക"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ദ്രുത ക്രമീകരണ എഡിറ്റർ."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> അറിയിപ്പ്: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 66e3ec3ba78a..7584173d48c5 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -889,10 +889,10 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Показывать значки уведомлений с низким приоритетом"</string>
<string name="other" msgid="429768510980739978">"Другое"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"удалить кнопку быстрого доступа"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"добавить кнопку быстрого доступа в конец"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Переместить кнопку быстрого доступа"</string>
- <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Добавить кнопку быстрого доступа"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"удалить панель"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"добавить панель в конец"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Переместить панель"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Добавить панель"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Переместить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index abcde4286190..5172303032b7 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Shfaq ikonat e njoftimeve me përparësi të ulët"</string>
<string name="other" msgid="429768510980739978">"Të tjera"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"hiq pllakëzën"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"shto pllakëzën në fund"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Zhvendos pllakëzën"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Shto pllakëzën"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Zhvendos te <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Shto te pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redaktori i cilësimeve të shpejta."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Njoftim nga <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Hap cilësimet."</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 9d5c54fc7890..d91f1fda3f0c 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"குறைந்த முன்னுரிமை உள்ள அறிவிப்பு ஐகான்களைக் காட்டு"</string>
<string name="other" msgid="429768510980739978">"மற்றவை"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"டைலை அகற்றும்"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"கடைசியில் டைலைச் சேர்க்கும்"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"டைலை நகர்த்து"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"டைலைச் சேர்"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> இடத்திற்கு நகர்த்தும்"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> என்ற இடத்தில் சேர்க்கும்"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"இடம்: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"விரைவு அமைப்புகள் திருத்தி."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> அறிவிப்பு: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"அமைப்புகளைத் திற."</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index eeedac603ab5..26053af9936f 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -889,10 +889,10 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Показувати значки сповіщень із низьким пріоритетом"</string>
<string name="other" msgid="429768510980739978">"Інше"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"вилучити значок"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додати значок у кінець"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перемістити значок"</string>
- <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додати значок"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"вилучити опцію"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додати опцію в кінець"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перемістити опцію"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додати опцію"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перемістити на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додати на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index facb47a3bdfc..dbb9e03ac279 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -880,8 +880,8 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
<string name="other" msgid="429768510980739978">"其他"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"將圖塊加到結尾"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移圖塊"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"將圖塊加到最尾"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"加圖塊"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移去 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"加去位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 48da5d90efe0..2e56df3e1fb8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -506,6 +506,10 @@
<dimen name="qs_header_tile_margin_bottom">18dp</dimen>
<dimen name="qs_page_indicator_width">16dp</dimen>
<dimen name="qs_page_indicator_height">8dp</dimen>
+ <!-- The size of a single dot in relation to the whole animation.
+ Scaled @dimen/qs_page_indicator-width by .4f.
+ -->
+ <dimen name="qs_page_indicator_dot_width">6.4dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">12sp</dimen>
<dimen name="qs_tile_divider_height">1dp</dimen>
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 cffc10f65f1e..ee05c6cf4c47 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
@@ -373,19 +373,6 @@ public class ActivityManagerWrapper {
}
/**
- * Moves an already resumed task to the side of the screen to initiate split screen.
- */
- public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
- Rect initialBounds) {
- try {
- return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(taskId,
- true /* onTop */);
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
* Registers a task stack listener with the system.
* This should be called on the main thread.
*/
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
new file mode 100644
index 000000000000..27cb4f60bd9c
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -0,0 +1,71 @@
+/*
+ * 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.shared.system;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.view.View;
+
+import com.android.internal.jank.InteractionJankMonitor;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public final class InteractionJankMonitorWrapper {
+ // Launcher journeys.
+ public static final int CUJ_APP_LAUNCH_FROM_RECENTS =
+ InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS;
+ public static final int CUJ_APP_LAUNCH_FROM_ICON =
+ InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON;
+ public static final int CUJ_APP_CLOSE_TO_HOME =
+ InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_HOME;
+ public static final int CUJ_APP_CLOSE_TO_PIP =
+ InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP;
+ public static final int CUJ_QUICK_SWITCH =
+ InteractionJankMonitor.CUJ_LAUNCHER_QUICK_SWITCH;
+
+ @IntDef({
+ CUJ_APP_LAUNCH_FROM_RECENTS,
+ CUJ_APP_LAUNCH_FROM_ICON,
+ CUJ_APP_CLOSE_TO_HOME,
+ CUJ_APP_CLOSE_TO_PIP,
+ CUJ_QUICK_SWITCH,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CujType {
+ }
+
+ public static void init(@NonNull View view) {
+ InteractionJankMonitor.getInstance().init(view);
+ }
+
+ public static boolean begin(@CujType int cujType) {
+ return InteractionJankMonitor.getInstance().begin(cujType);
+ }
+
+ public static boolean begin(@CujType int cujType, long timeout) {
+ return InteractionJankMonitor.getInstance().begin(cujType, timeout);
+ }
+
+ public static boolean end(@CujType int cujType) {
+ return InteractionJankMonitor.getInstance().end(cujType);
+ }
+
+ public static boolean cancel(@CujType int cujType) {
+ return InteractionJankMonitor.getInstance().cancel(cujType);
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 2985a61dec9e..86129e0e204e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -47,6 +47,7 @@ public class SyncRtSurfaceTransactionApplierCompat {
public static final int FLAG_BACKGROUND_BLUR_RADIUS = 1 << 5;
public static final int FLAG_VISIBILITY = 1 << 6;
public static final int FLAG_RELATIVE_LAYER = 1 << 7;
+ public static final int FLAG_SHADOW_RADIUS = 1 << 8;
private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
@@ -196,6 +197,7 @@ public class SyncRtSurfaceTransactionApplierCompat {
SurfaceControl relativeTo;
int relativeLayer;
boolean visible;
+ float shadowRadius;
/**
* @param surface The surface to modify.
@@ -274,6 +276,16 @@ public class SyncRtSurfaceTransactionApplierCompat {
}
/**
+ * @param radius the Radius for the shadows to apply to the surface.
+ * @return this Builder
+ */
+ public Builder withShadowRadius(float radius) {
+ this.shadowRadius = radius;
+ flags |= FLAG_SHADOW_RADIUS;
+ return this;
+ }
+
+ /**
* @param radius the Radius for blur to apply to the background surfaces.
* @return this Builder
*/
@@ -298,31 +310,14 @@ public class SyncRtSurfaceTransactionApplierCompat {
*/
public SurfaceParams build() {
return new SurfaceParams(surface, flags, alpha, matrix, windowCrop, layer,
- relativeTo, relativeLayer, cornerRadius, backgroundBlurRadius, visible);
+ relativeTo, relativeLayer, cornerRadius, backgroundBlurRadius, visible,
+ shadowRadius);
}
}
- /**
- * Constructs surface parameters to be applied when the current view state gets pushed to
- * RenderThread.
- *
- * @param surface The surface to modify.
- * @param alpha Alpha to apply.
- * @param matrix Matrix to apply.
- * @param windowCrop Crop to apply, only applied if not {@code null}
- */
- public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
- Rect windowCrop, int layer, SurfaceControl relativeTo, int relativeLayer,
- float cornerRadius) {
- this(surface.mSurfaceControl,
- FLAG_ALL & ~(FLAG_VISIBILITY | FLAG_BACKGROUND_BLUR_RADIUS), alpha,
- matrix, windowCrop, layer, relativeTo, relativeLayer, cornerRadius,
- 0 /* backgroundBlurRadius */, true);
- }
-
private SurfaceParams(SurfaceControl surface, int flags, float alpha, Matrix matrix,
Rect windowCrop, int layer, SurfaceControl relativeTo, int relativeLayer,
- float cornerRadius, int backgroundBlurRadius, boolean visible) {
+ float cornerRadius, int backgroundBlurRadius, boolean visible, float shadowRadius) {
this.flags = flags;
this.surface = surface;
this.alpha = alpha;
@@ -334,6 +329,7 @@ public class SyncRtSurfaceTransactionApplierCompat {
this.cornerRadius = cornerRadius;
this.backgroundBlurRadius = backgroundBlurRadius;
this.visible = visible;
+ this.shadowRadius = shadowRadius;
}
private final int flags;
@@ -349,6 +345,7 @@ public class SyncRtSurfaceTransactionApplierCompat {
public final SurfaceControl relativeTo;
public final int relativeLayer;
public final boolean visible;
+ public final float shadowRadius;
public void applyTo(SurfaceControl.Transaction t) {
if ((flags & FLAG_MATRIX) != 0) {
@@ -379,6 +376,9 @@ public class SyncRtSurfaceTransactionApplierCompat {
if ((flags & FLAG_RELATIVE_LAYER) != 0) {
t.setRelativeLayer(surface, relativeTo, relativeLayer);
}
+ if ((flags & FLAG_SHADOW_RADIUS) != 0) {
+ t.setShadowRadius(surface, shadowRadius);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index e99245fa438f..23195af8bdea 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -33,9 +33,13 @@ import android.view.SurfaceView;
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.systemui.dagger.qualifiers.Main;
import java.util.NoSuchElementException;
+import javax.inject.Inject;
+
/**
* Encapsulates all logic for secondary lockscreen state management.
*/
@@ -142,9 +146,9 @@ public class AdminSecondaryLockScreenController {
}
};
- public AdminSecondaryLockScreenController(Context context, ViewGroup parent,
+ private AdminSecondaryLockScreenController(Context context, KeyguardSecurityContainer parent,
KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback,
- Handler handler) {
+ @Main Handler handler) {
mContext = context;
mHandler = handler;
mParent = parent;
@@ -234,4 +238,26 @@ public class AdminSecondaryLockScreenController {
getHolder().removeCallback(mSurfaceHolderCallback);
}
}
+
+ @KeyguardBouncerScope
+ public static class Factory {
+ private final Context mContext;
+ private final KeyguardSecurityContainer mParent;
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final Handler mHandler;
+
+ @Inject
+ public Factory(Context context, KeyguardSecurityContainer parent,
+ KeyguardUpdateMonitor updateMonitor, @Main Handler handler) {
+ mContext = context;
+ mParent = parent;
+ mUpdateMonitor = updateMonitor;
+ mHandler = handler;
+ }
+
+ public AdminSecondaryLockScreenController create(KeyguardSecurityCallback callback) {
+ return new AdminSecondaryLockScreenController(mContext, mParent, mUpdateMonitor,
+ callback, mHandler);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 88f4176f5eac..cc6df45c598f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -16,46 +16,26 @@
package com.android.keyguard;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
-
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.os.AsyncTask;
-import android.os.CountDownTimer;
-import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.View;
-import android.widget.LinearLayout;
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
* Base class for PIN and password unlock screens.
*/
-public abstract class KeyguardAbsKeyInputView extends LinearLayout
- implements KeyguardSecurityView, EmergencyButton.EmergencyButtonCallback {
- protected KeyguardSecurityCallback mCallback;
- protected LockPatternUtils mLockPatternUtils;
- protected AsyncTask<?, ?, ?> mPendingLockCheck;
- protected SecurityMessageDisplay mSecurityMessageDisplay;
+public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
protected View mEcaView;
protected boolean mEnableHaptics;
- private boolean mDismissing;
- protected boolean mResumed;
- private CountDownTimer mCountdownTimer = null;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
// To avoid accidental lockout due to events while the device in in the pocket, ignore
// any passwords with length less than or equal to this length.
protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
+ private KeyDownListener mKeyDownListener;
public KeyguardAbsKeyInputView(Context context) {
this(context, null);
@@ -63,38 +43,10 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
super(context, attrs);
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
}
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mCallback = callback;
- }
-
- @Override
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
- mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled();
- }
-
- @Override
- public void reset() {
- // start fresh
- mDismissing = false;
- resetPasswordText(false /* animate */, false /* announce */);
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
- if (shouldLockout(deadline)) {
- handleAttemptLockout(deadline);
- } else {
- resetState();
- }
- }
-
- // Allow subclasses to override this behavior
- protected boolean shouldLockout(long deadline) {
- return deadline != 0;
+ void setEnableHaptics(boolean enableHaptics) {
+ mEnableHaptics = enableHaptics;
}
protected abstract int getPasswordTextViewId();
@@ -102,24 +54,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
@Override
protected void onFinishInflate() {
- mLockPatternUtils = new LockPatternUtils(mContext);
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
-
- EmergencyButton button = findViewById(R.id.emergency_call_button);
- if (button != null) {
- button.setCallback(this);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this);
- }
-
- @Override
- public void onEmergencyButtonClickedWhenInCall() {
- mCallback.reset();
}
/*
@@ -131,195 +66,14 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
return R.string.kg_wrong_password;
}
- protected void verifyPasswordAndUnlock() {
- if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
-
- final LockscreenCredential password = getEnteredCredential();
- setPasswordEntryInputEnabled(false);
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- }
-
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
- if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
- // to avoid accidental lockout, only count attempts that are long enough to be a
- // real password. This may require some tweaking.
- setPasswordEntryInputEnabled(true);
- onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
- password.zeroize();
- return;
- }
-
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
- LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
-
- mKeyguardUpdateMonitor.setCredentialAttempted();
- mPendingLockCheck = LockPatternChecker.checkCredential(
- mLockPatternUtils,
- password,
- userId,
- new LockPatternChecker.OnCheckCallback() {
-
- @Override
- public void onEarlyMatched() {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL);
- }
- onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
- true /* isValidPassword */);
- password.zeroize();
- }
-
- @Override
- public void onChecked(boolean matched, int timeoutMs) {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- setPasswordEntryInputEnabled(true);
- mPendingLockCheck = null;
- if (!matched) {
- onPasswordChecked(userId, false /* matched */, timeoutMs,
- true /* isValidPassword */);
- }
- password.zeroize();
- }
-
- @Override
- public void onCancelled() {
- // We already got dismissed with the early matched callback, so we cancelled
- // the check. However, we still need to note down the latency.
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- password.zeroize();
- }
- });
- }
-
- private void onPasswordChecked(int userId, boolean matched, int timeoutMs,
- boolean isValidPassword) {
- boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
- if (matched) {
- mCallback.reportUnlockAttempt(userId, true, 0);
- if (dismissKeyguard) {
- mDismissing = true;
- mCallback.dismiss(true, userId);
- }
- } else {
- if (isValidPassword) {
- mCallback.reportUnlockAttempt(userId, false, timeoutMs);
- if (timeoutMs > 0) {
- long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
- userId, timeoutMs);
- handleAttemptLockout(deadline);
- }
- }
- if (timeoutMs == 0) {
- mSecurityMessageDisplay.setMessage(getWrongPasswordStringId());
- }
- }
- resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
- }
-
protected abstract void resetPasswordText(boolean animate, boolean announce);
protected abstract LockscreenCredential getEnteredCredential();
protected abstract void setPasswordEntryEnabled(boolean enabled);
protected abstract void setPasswordEntryInputEnabled(boolean enabled);
- // Prevent user from using the PIN/Password entry until scheduled deadline.
- protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
- setPasswordEntryEnabled(false);
- long elapsedRealtime = SystemClock.elapsedRealtime();
- long secondsInFuture = (long) Math.ceil(
- (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
- mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
-
- @Override
- public void onTick(long millisUntilFinished) {
- int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
- mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString(
- R.plurals.kg_too_many_failed_attempts_countdown,
- secondsRemaining, secondsRemaining));
- }
-
- @Override
- public void onFinish() {
- mSecurityMessageDisplay.setMessage("");
- resetState();
- }
- }.start();
- }
-
- protected void onUserInput() {
- if (mCallback != null) {
- mCallback.userActivity();
- mCallback.onUserInput();
- }
- mSecurityMessageDisplay.setMessage("");
- }
-
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
- // We don't want to consider it valid user input because the UI
- // will already respond to the event.
- if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
- onUserInput();
- }
- return false;
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public void onPause() {
- mResumed = false;
-
- if (mCountdownTimer != null) {
- mCountdownTimer.cancel();
- mCountdownTimer = null;
- }
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- mPendingLockCheck = null;
- }
- reset();
- }
-
- @Override
- public void onResume(int reason) {
- mResumed = true;
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
- @Override
- public void showPromptReason(int reason) {
- if (reason != PROMPT_REASON_NONE) {
- int promtReasonStringRes = getPromptReasonStringRes(reason);
- if (promtReasonStringRes != 0) {
- mSecurityMessageDisplay.setMessage(promtReasonStringRes);
- }
- }
- }
-
- @Override
- public void showMessage(CharSequence message, ColorStateList colorState) {
- if (colorState != null) {
- mSecurityMessageDisplay.setNextMessageColor(colorState);
- }
- mSecurityMessageDisplay.setMessage(message);
+ return mKeyDownListener != null && mKeyDownListener.onKeyDown(keyCode, event);
}
protected abstract int getPromptReasonStringRes(int reason);
@@ -333,9 +87,12 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
}
}
- @Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- return false;
+ public void setKeyDownListener(KeyDownListener keyDownListener) {
+ mKeyDownListener = keyDownListener;
+ }
+
+ public interface KeyDownListener {
+ boolean onKeyDown(int keyCode, KeyEvent keyEvent);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
new file mode 100644
index 000000000000..53f847434dcc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -0,0 +1,275 @@
+/*
+ * 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.keyguard;
+
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;
+
+import android.content.res.ColorStateList;
+import android.os.AsyncTask;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.view.KeyEvent;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.keyguard.EmergencyButton.EmergencyButtonCallback;
+import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView>
+ extends KeyguardInputViewController<T> {
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final LockPatternUtils mLockPatternUtils;
+ private final LatencyTracker mLatencyTracker;
+ private CountDownTimer mCountdownTimer;
+ protected KeyguardMessageAreaController mMessageAreaController;
+ private boolean mDismissing;
+ protected AsyncTask<?, ?, ?> mPendingLockCheck;
+ protected boolean mResumed;
+
+ private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> {
+ // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
+ // We don't want to consider it valid user input because the UI
+ // will already respond to the event.
+ if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
+ onUserInput();
+ }
+ return false;
+ };
+
+ private final EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
+ @Override
+ public void onEmergencyButtonClickedWhenInCall() {
+ getKeyguardSecurityCallback().reset();
+ }
+ };
+
+ protected KeyguardAbsKeyInputViewController(T view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode,
+ LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker) {
+ super(view, securityMode, keyguardSecurityCallback);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
+ mLatencyTracker = latencyTracker;
+ KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
+ mMessageAreaController = messageAreaControllerFactory.create(kma);
+ }
+
+ abstract void resetState();
+
+ @Override
+ public void init() {
+ super.init();
+ mMessageAreaController.init();
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mView.setKeyDownListener(mKeyDownListener);
+ mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
+ EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
+ if (button != null) {
+ button.setCallback(mEmergencyButtonCallback);
+ }
+ }
+
+ @Override
+ public void reset() {
+ // start fresh
+ mDismissing = false;
+ mView.resetPasswordText(false /* animate */, false /* announce */);
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (shouldLockout(deadline)) {
+ handleAttemptLockout(deadline);
+ } else {
+ resetState();
+ }
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (colorState != null) {
+ mMessageAreaController.setNextMessageColor(colorState);
+ }
+ mMessageAreaController.setMessage(message);
+ }
+
+ // Allow subclasses to override this behavior
+ protected boolean shouldLockout(long deadline) {
+ return deadline != 0;
+ }
+
+ // Prevent user from using the PIN/Password entry until scheduled deadline.
+ protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ mView.setPasswordEntryEnabled(false);
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ long secondsInFuture = (long) Math.ceil(
+ (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
+ mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
+ mMessageAreaController.setMessage(mView.getResources().getQuantityString(
+ R.plurals.kg_too_many_failed_attempts_countdown,
+ secondsRemaining, secondsRemaining));
+ }
+
+ @Override
+ public void onFinish() {
+ mMessageAreaController.setMessage("");
+ resetState();
+ }
+ }.start();
+ }
+
+ void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) {
+ boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
+ if (matched) {
+ getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
+ if (dismissKeyguard) {
+ mDismissing = true;
+ getKeyguardSecurityCallback().dismiss(true, userId);
+ }
+ } else {
+ if (isValidPassword) {
+ getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
+ if (timeoutMs > 0) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
+ userId, timeoutMs);
+ handleAttemptLockout(deadline);
+ }
+ }
+ if (timeoutMs == 0) {
+ mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
+ }
+ }
+ mView.resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
+ }
+
+ protected void verifyPasswordAndUnlock() {
+ if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
+
+ final LockscreenCredential password = mView.getEnteredCredential();
+ mView.setPasswordEntryInputEnabled(false);
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ }
+
+ final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
+ // to avoid accidental lockout, only count attempts that are long enough to be a
+ // real password. This may require some tweaking.
+ mView.setPasswordEntryInputEnabled(true);
+ onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
+ password.zeroize();
+ return;
+ }
+
+ mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL);
+ mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+
+ mKeyguardUpdateMonitor.setCredentialAttempted();
+ mPendingLockCheck = LockPatternChecker.checkCredential(
+ mLockPatternUtils,
+ password,
+ userId,
+ new LockPatternChecker.OnCheckCallback() {
+
+ @Override
+ public void onEarlyMatched() {
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL);
+
+ onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
+ true /* isValidPassword */);
+ password.zeroize();
+ }
+
+ @Override
+ public void onChecked(boolean matched, int timeoutMs) {
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ mView.setPasswordEntryInputEnabled(true);
+ mPendingLockCheck = null;
+ if (!matched) {
+ onPasswordChecked(userId, false /* matched */, timeoutMs,
+ true /* isValidPassword */);
+ }
+ password.zeroize();
+ }
+
+ @Override
+ public void onCancelled() {
+ // We already got dismissed with the early matched callback, so we cancelled
+ // the check. However, we still need to note down the latency.
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ password.zeroize();
+ }
+ });
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ if (reason != PROMPT_REASON_NONE) {
+ int promtReasonStringRes = mView.getPromptReasonStringRes(reason);
+ if (promtReasonStringRes != 0) {
+ mMessageAreaController.setMessage(promtReasonStringRes);
+ }
+ }
+ }
+
+ protected void onUserInput() {
+ getKeyguardSecurityCallback().userActivity();
+ getKeyguardSecurityCallback().onUserInput();
+ mMessageAreaController.setMessage("");
+ }
+
+ @Override
+ public void onResume(int reason) {
+ mResumed = true;
+ }
+
+ @Override
+ public void onPause() {
+ mResumed = false;
+
+ if (mCountdownTimer != null) {
+ mCountdownTimer.cancel();
+ mCountdownTimer = null;
+ }
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ mPendingLockCheck = null;
+ }
+ reset();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index be21d203411e..36d5543f1c01 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -39,7 +39,6 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
-import com.android.systemui.util.InjectionInflationController;
import javax.inject.Inject;
@@ -49,7 +48,6 @@ public class KeyguardDisplayManager {
private final MediaRouter mMediaRouter;
private final DisplayManager mDisplayService;
- private final InjectionInflationController mInjectableInflater;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
private final Context mContext;
@@ -92,10 +90,8 @@ public class KeyguardDisplayManager {
@Inject
public KeyguardDisplayManager(Context context,
- InjectionInflationController injectableInflater,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
mContext = context;
- mInjectableInflater = injectableInflater;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mMediaRouter = mContext.getSystemService(MediaRouter.class);
mDisplayService = mContext.getSystemService(DisplayManager.class);
@@ -131,8 +127,7 @@ public class KeyguardDisplayManager {
Presentation presentation = mPresentations.get(displayId);
if (presentation == null) {
final Presentation newPresentation = new KeyguardPresentation(mContext, display,
- mKeyguardStatusViewComponentFactory,
- mInjectableInflater.injectable(LayoutInflater.from(mContext)));
+ mKeyguardStatusViewComponentFactory, LayoutInflater.from(mContext));
newPresentation.setOnDismissListener(dialog -> {
if (newPresentation.equals(mPresentations.get(displayId))) {
mPresentations.remove(displayId);
@@ -250,7 +245,7 @@ public class KeyguardDisplayManager {
private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- private final LayoutInflater mInjectableLayoutInflater;
+ private final LayoutInflater mLayoutInflater;
private KeyguardClockSwitchController mKeyguardClockSwitchController;
private View mClock;
private int mUsableWidth;
@@ -270,10 +265,10 @@ public class KeyguardDisplayManager {
KeyguardPresentation(Context context, Display display,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
- LayoutInflater injectionLayoutInflater) {
+ LayoutInflater layoutInflater) {
super(context, display, R.style.Theme_SystemUI_KeyguardPresentation);
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
- mInjectableLayoutInflater = injectionLayoutInflater;
+ mLayoutInflater = layoutInflater;
getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
setCancelable(false);
}
@@ -299,7 +294,7 @@ public class KeyguardDisplayManager {
mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
- setContentView(mInjectableLayoutInflater.inflate(R.layout.keyguard_presentation, null));
+ setContentView(mLayoutInflater.inflate(R.layout.keyguard_presentation, null));
// Logic to make the lock screen fullscreen
getWindow().getDecorView().setSystemUiVisibility(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 7aabb17de90c..351369c51364 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -163,33 +163,34 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
@Inject
public KeyguardHostViewController(KeyguardHostView view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardSecurityContainerController keyguardSecurityContainerController,
AudioManager audioManager,
TelephonyManager telephonyManager,
- ViewMediatorCallback viewMediatorCallback) {
+ ViewMediatorCallback viewMediatorCallback,
+ KeyguardSecurityContainerController.Factory
+ keyguardSecurityContainerControllerFactory) {
super(view);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mKeyguardSecurityContainerController = keyguardSecurityContainerController;
mAudioManager = audioManager;
mTelephonyManager = telephonyManager;
mViewMediatorCallback = viewMediatorCallback;
+ mKeyguardSecurityContainerController = keyguardSecurityContainerControllerFactory.create(
+ mSecurityCallback);
}
/** Initialize the Controller. */
public void init() {
super.init();
- mView.setViewMediatorCallback(mViewMediatorCallback);
- // Update ViewMediator with the current input method requirements
- mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput());
mKeyguardSecurityContainerController.init();
- mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback);
- mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
}
@Override
protected void onViewAttached() {
+ mView.setViewMediatorCallback(mViewMediatorCallback);
+ // Update ViewMediator with the current input method requirements
+ mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput());
mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
mView.setOnKeyListener(mOnKeyListener);
+ mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
}
@Override
@@ -350,7 +351,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
}
public boolean handleBackKey() {
- if (mKeyguardSecurityContainerController.getCurrentSecuritySelection()
+ if (mKeyguardSecurityContainerController.getCurrentSecurityMode()
!= SecurityMode.None) {
mKeyguardSecurityContainerController.dismiss(
false, KeyguardUpdateMonitor.getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
new file mode 100644
index 000000000000..d42a53cc875e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+/**
+ * A Base class for all Keyguard password/pattern/pin related inputs.
+ */
+public abstract class KeyguardInputView extends LinearLayout {
+
+ public KeyguardInputView(Context context) {
+ super(context);
+ }
+
+ public KeyguardInputView(Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public KeyguardInputView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ abstract CharSequence getTitle();
+
+ boolean disallowInterceptTouch(MotionEvent event) {
+ return false;
+ }
+
+ void startAppearAnimation() {}
+
+ boolean startDisappearAnimation(Runnable finishRunnable) {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
new file mode 100644
index 000000000000..fbda818740e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -0,0 +1,196 @@
+/*
+ * 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.keyguard;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.telephony.TelephonyManager;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.ViewController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import javax.inject.Inject;
+
+
+/** Controller for a {@link KeyguardSecurityView}. */
+public abstract class KeyguardInputViewController<T extends KeyguardInputView>
+ extends ViewController<T> implements KeyguardSecurityView {
+
+ private final SecurityMode mSecurityMode;
+ private final KeyguardSecurityCallback mKeyguardSecurityCallback;
+ private boolean mPaused;
+
+
+ // The following is used to ignore callbacks from SecurityViews that are no longer current
+ // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
+ // state for the current security method.
+ private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
+ @Override
+ public void userActivity() { }
+ @Override
+ public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
+ @Override
+ public boolean isVerifyUnlockOnly() {
+ return false;
+ }
+ @Override
+ public void dismiss(boolean securityVerified, int targetUserId) { }
+ @Override
+ public void dismiss(boolean authenticated, int targetId,
+ boolean bypassSecondaryLockScreen) { }
+ @Override
+ public void onUserInput() { }
+ @Override
+ public void reset() {}
+ };
+
+ protected KeyguardInputViewController(T view, SecurityMode securityMode,
+ KeyguardSecurityCallback keyguardSecurityCallback) {
+ super(view);
+ mSecurityMode = securityMode;
+ mKeyguardSecurityCallback = keyguardSecurityCallback;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ }
+
+ @Override
+ protected void onViewDetached() {
+ }
+
+ SecurityMode getSecurityMode() {
+ return mSecurityMode;
+ }
+
+ protected KeyguardSecurityCallback getKeyguardSecurityCallback() {
+ if (mPaused) {
+ return mNullCallback;
+ }
+
+ return mKeyguardSecurityCallback;
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ public void onPause() {
+ mPaused = true;
+ }
+
+ @Override
+ public void onResume(int reason) {
+ mPaused = false;
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ }
+
+ @Override
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ }
+
+ public void startAppearAnimation() {
+ mView.startAppearAnimation();
+ }
+
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return mView.startDisappearAnimation(finishRunnable);
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mView.getTitle();
+ }
+
+ /** Finds the index of this view in the suppplied parent view. */
+ public int getIndexIn(KeyguardSecurityViewFlipper view) {
+ return view.indexOfChild(mView);
+ }
+
+ /** Factory for a {@link KeyguardInputViewController}. */
+ public static class Factory {
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final LockPatternUtils mLockPatternUtils;
+ private final LatencyTracker mLatencyTracker;
+ private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
+ private final InputMethodManager mInputMethodManager;
+ private final DelayableExecutor mMainExecutor;
+ private final Resources mResources;
+ private LiftToActivateListener mLiftToActivateListener;
+ private TelephonyManager mTelephonyManager;
+
+ @Inject
+ public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
+ LockPatternUtils lockPatternUtils,
+ LatencyTracker latencyTracker,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
+ @Main Resources resources, LiftToActivateListener liftToActivateListener,
+ TelephonyManager telephonyManager) {
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
+ mLatencyTracker = latencyTracker;
+ mMessageAreaControllerFactory = messageAreaControllerFactory;
+ mInputMethodManager = inputMethodManager;
+ mMainExecutor = mainExecutor;
+ mResources = resources;
+ mLiftToActivateListener = liftToActivateListener;
+ mTelephonyManager = telephonyManager;
+ }
+
+ /** Create a new {@link KeyguardInputViewController}. */
+ public KeyguardInputViewController create(KeyguardInputView keyguardInputView,
+ SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback) {
+ if (keyguardInputView instanceof KeyguardPatternView) {
+ return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mLatencyTracker, mMessageAreaControllerFactory);
+ } else if (keyguardInputView instanceof KeyguardPasswordView) {
+ return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
+ mInputMethodManager, mMainExecutor, mResources);
+ } else if (keyguardInputView instanceof KeyguardPINView) {
+ return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
+ mLiftToActivateListener);
+ } else if (keyguardInputView instanceof KeyguardSimPinView) {
+ return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
+ mLiftToActivateListener, mTelephonyManager);
+ } else if (keyguardInputView instanceof KeyguardSimPukView) {
+ return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
+ mLiftToActivateListener, mTelephonyManager);
+ }
+
+ throw new RuntimeException("Unable to find controller for " + keyguardInputView);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index a8b1451d92c7..1a0a4370fca4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -16,8 +16,6 @@
package com.android.keyguard;
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
@@ -31,20 +29,14 @@ import android.util.TypedValue;
import android.view.View;
import android.widget.TextView;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import java.lang.ref.WeakReference;
-import javax.inject.Inject;
-import javax.inject.Named;
-
/***
* Manages a number of views inside of the given layout. See below for a list of widgets.
*/
-public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay,
- ConfigurationController.ConfigurationListener {
+public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
/** Handler token posted with accessibility announcement runnables. */
private static final Object ANNOUNCE_TOKEN = new Object();
@@ -56,71 +48,26 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp
private static final int DEFAULT_COLOR = -1;
private final Handler mHandler;
- private final ConfigurationController mConfigurationController;
private ColorStateList mDefaultColorState;
private CharSequence mMessage;
private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
private boolean mBouncerVisible;
- private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
- public void onFinishedGoingToSleep(int why) {
- setSelected(false);
- }
-
- public void onStartedWakingUp() {
- setSelected(true);
- }
-
- @Override
- public void onKeyguardBouncerChanged(boolean bouncer) {
- mBouncerVisible = bouncer;
- update();
- }
- };
-
- public KeyguardMessageArea(Context context) {
- super(context, null);
- throw new IllegalStateException("This constructor should never be invoked");
- }
-
- @Inject
- public KeyguardMessageArea(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- ConfigurationController configurationController) {
- this(context, attrs, Dependency.get(KeyguardUpdateMonitor.class), configurationController);
- }
-
- public KeyguardMessageArea(Context context, AttributeSet attrs, KeyguardUpdateMonitor monitor,
- ConfigurationController configurationController) {
+ public KeyguardMessageArea(Context context, AttributeSet attrs) {
super(context, attrs);
setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
- monitor.registerCallback(mInfoCallback);
mHandler = new Handler(Looper.myLooper());
- mConfigurationController = configurationController;
onThemeChanged();
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mConfigurationController.addCallback(this);
- onThemeChanged();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mConfigurationController.removeCallback(this);
- }
-
- @Override
public void setNextMessageColor(ColorStateList colorState) {
mNextMessageColorState = colorState;
}
- @Override
- public void onThemeChanged() {
+ void onThemeChanged() {
TypedArray array = mContext.obtainStyledAttributes(new int[] {
R.attr.wallpaperTextColor
});
@@ -130,8 +77,7 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp
update();
}
- @Override
- public void onDensityOrFontScaleChanged() {
+ void onDensityOrFontScaleChanged() {
TypedArray array = mContext.obtainStyledAttributes(R.style.Keyguard_TextView, new int[] {
android.R.attr.textSize
});
@@ -177,12 +123,6 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp
return messageArea;
}
- @Override
- protected void onFinishInflate() {
- boolean shouldMarquee = Dependency.get(KeyguardUpdateMonitor.class).isDeviceInteractive();
- setSelected(shouldMarquee); // This is required to ensure marquee works
- }
-
private void securityMessageChanged(CharSequence message) {
mMessage = message;
update();
@@ -196,7 +136,7 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp
update();
}
- private void update() {
+ void update() {
CharSequence status = mMessage;
setVisibility(TextUtils.isEmpty(status) || !mBouncerVisible ? INVISIBLE : VISIBLE);
setText(status);
@@ -208,6 +148,9 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp
setTextColor(colorState);
}
+ public void setBouncerVisible(boolean bouncerVisible) {
+ mBouncerVisible = bouncerVisible;
+ }
/**
* Runnable used to delay accessibility announcements.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index f056bdbb9706..1618e8e58055 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -16,7 +16,10 @@
package com.android.keyguard;
+import android.content.res.ColorStateList;
+
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -26,6 +29,35 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
+
+ private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
+ public void onFinishedGoingToSleep(int why) {
+ mView.setSelected(false);
+ }
+
+ public void onStartedWakingUp() {
+ mView.setSelected(true);
+ }
+
+ @Override
+ public void onKeyguardBouncerChanged(boolean bouncer) {
+ mView.setBouncerVisible(bouncer);
+ mView.update();
+ }
+ };
+
+ private ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+ @Override
+ public void onThemeChanged() {
+ mView.onThemeChanged();
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mView.onDensityOrFontScaleChanged();
+ }
+ };
+
private KeyguardMessageAreaController(KeyguardMessageArea view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController) {
@@ -37,17 +69,31 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag
@Override
protected void onViewAttached() {
- //mConfigurationController.addCallback();
- //mKeyguardUpdateMonitor.registerCallback();
+ mConfigurationController.addCallback(mConfigurationListener);
+ mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
+ mView.setSelected(mKeyguardUpdateMonitor.isDeviceInteractive());
+ mView.onThemeChanged();
}
@Override
protected void onViewDetached() {
- //mConfigurationController.removeCallback();
- //mKeyguardUpdateMonitor.removeCallback();
+ mConfigurationController.removeCallback(mConfigurationListener);
+ mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
+ }
+
+ public void setMessage(CharSequence s) {
+ mView.setMessage(s);
+ }
+
+ public void setMessage(int resId) {
+ mView.setMessage(resId);
+ }
+
+ public void setNextMessageColor(ColorStateList colorState) {
+ mView.setNextMessageColor(colorState);
}
- /** Factory for createing {@link com.android.keyguard.KeyguardMessageAreaController}. */
+ /** Factory for creating {@link com.android.keyguard.KeyguardMessageAreaController}. */
public static class Factory {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 12ea1d586e10..580d7043a220 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -24,7 +24,6 @@ import android.view.animation.AnimationUtils;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
@@ -40,10 +39,8 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
private ViewGroup mRow1;
private ViewGroup mRow2;
private ViewGroup mRow3;
- private View mDivider;
private int mDisappearYTranslation;
private View[][] mViews;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
public KeyguardPINView(Context context) {
this(context, null);
@@ -63,15 +60,10 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
}
@Override
protected void resetState() {
- super.resetState();
- if (mSecurityMessageDisplay != null) {
- mSecurityMessageDisplay.setMessage("");
- }
}
@Override
@@ -88,7 +80,6 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
mRow1 = findViewById(R.id.row1);
mRow2 = findViewById(R.id.row2);
mRow3 = findViewById(R.id.row3);
- mDivider = findViewById(R.id.divider);
mViews = new View[][]{
new View[]{
mRow0, null, null
@@ -112,18 +103,6 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
new View[]{
null, mEcaView, null
}};
-
- View cancelBtn = findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(view -> {
- mCallback.reset();
- mCallback.onCancelClicked();
- });
- }
- }
-
- @Override
- public void showUsabilityHint() {
}
@Override
@@ -147,24 +126,21 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
});
}
- @Override
- public boolean startDisappearAnimation(final Runnable finishRunnable) {
+ public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
+ final Runnable finishRunnable) {
+
enableClipping(false);
setTranslationY(0);
AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 280 /* duration */,
mDisappearYTranslation, mDisappearAnimationUtils.getInterpolator());
- DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor
- .needsSlowUnlockTransition()
+ DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
? mDisappearAnimationUtilsLocked
: mDisappearAnimationUtils;
disappearAnimationUtils.startAnimation2d(mViews,
- new Runnable() {
- @Override
- public void run() {
- enableClipping(true);
- if (finishRunnable != null) {
- finishRunnable.run();
- }
+ () -> {
+ enableClipping(true);
+ if (finishRunnable != null) {
+ finishRunnable.run();
}
});
return true;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 97317cf5580f..aaa5efec807e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -16,50 +16,37 @@
package com.android.keyguard;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+
import android.content.Context;
import android.graphics.Rect;
-import android.os.UserHandle;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.text.method.TextKeyListener;
import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
import android.widget.TextView;
-import android.widget.TextView.OnEditorActionListener;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.TextViewInputDisabler;
import com.android.systemui.R;
-
-import java.util.List;
/**
* Displays an alphanumeric (latin-1) key entry for the user to enter
* an unlock password
*/
-public class KeyguardPasswordView extends KeyguardAbsKeyInputView
- implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
- private final boolean mShowImeAtScreenOn;
private final int mDisappearYTranslation;
// A delay constant to be used in a workaround for the situation where InputMethodManagerService
// is not switched to the new user yet.
// TODO: Remove this by ensuring such a race condition never happens.
- private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
- InputMethodManager mImm;
private TextView mPasswordEntry;
private TextViewInputDisabler mPasswordEntryDisabler;
- private View mSwitchImeButton;
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
@@ -70,8 +57,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
public KeyguardPasswordView(Context context, AttributeSet attrs) {
super(context, attrs);
- mShowImeAtScreenOn = context.getResources().
- getBoolean(R.bool.kg_show_ime_at_screen_on);
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
@@ -82,20 +67,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
@Override
protected void resetState() {
- mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
- if (mSecurityMessageDisplay != null) {
- mSecurityMessageDisplay.setMessage("");
- }
- final boolean wasDisabled = mPasswordEntry.isEnabled();
- setPasswordEntryEnabled(true);
- setPasswordEntryInputEnabled(true);
- // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage.
- if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
- return;
- }
- if (wasDisabled) {
- mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
- }
}
@Override
@@ -104,29 +75,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
}
@Override
- public boolean needsInput() {
- return true;
- }
-
- @Override
- public void onResume(final int reason) {
- super.onResume(reason);
-
- // Wait a bit to focus the field so the focusable flag on the window is already set then.
- post(new Runnable() {
- @Override
- public void run() {
- if (isShown() && mPasswordEntry.isEnabled()) {
- mPasswordEntry.requestFocus();
- if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
- mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
- }
- }
- }
- });
- }
-
- @Override
protected int getPromptReasonStringRes(int reason) {
switch (reason) {
case PROMPT_REASON_RESTART:
@@ -146,97 +94,13 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
}
}
- @Override
- public void onPause() {
- super.onPause();
- mImm.hideSoftInputFromWindow(getWindowToken(), 0);
- }
-
- @Override
- public void onStartingToHide() {
- mImm.hideSoftInputFromWindow(getWindowToken(), 0);
- }
-
- private void updateSwitchImeButton() {
- // If there's more than one IME, enable the IME switcher button
- final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
- final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false);
- if (wasVisible != shouldBeVisible) {
- mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
- }
-
- // TODO: Check if we still need this hack.
- // If no icon is visible, reset the start margin on the password field so the text is
- // still centered.
- if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
- android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
- if (params instanceof MarginLayoutParams) {
- final MarginLayoutParams mlp = (MarginLayoutParams) params;
- mlp.setMarginStart(0);
- mPasswordEntry.setLayoutParams(params);
- }
- }
- }
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mImm = (InputMethodManager) getContext().getSystemService(
- Context.INPUT_METHOD_SERVICE);
-
mPasswordEntry = findViewById(getPasswordTextViewId());
- mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry);
- mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
- mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
- | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- mPasswordEntry.setOnEditorActionListener(this);
- mPasswordEntry.addTextChangedListener(this);
-
- // Poke the wakelock any time the text is selected or modified
- mPasswordEntry.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCallback.userActivity();
- }
- });
-
- // Set selected property on so the view can send accessibility events.
- mPasswordEntry.setSelected(true);
-
- mSwitchImeButton = findViewById(R.id.switch_ime_button);
- mSwitchImeButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCallback.userActivity(); // Leave the screen on a bit longer
- // Do not show auxiliary subtypes in password lock screen.
- mImm.showInputMethodPickerFromSystem(false /* showAuxiliarySubtypes */,
- getContext().getDisplayId());
- }
- });
-
- View cancelBtn = findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(view -> {
- mCallback.reset();
- mCallback.onCancelClicked();
- });
- }
-
- // If there's more than one IME, enable the IME switcher button
- updateSwitchImeButton();
-
- // When we the current user is switching, InputMethodManagerService sometimes has not
- // switched internal state yet here. As a quick workaround, we check the keyboard state
- // again.
- // TODO: Remove this workaround by ensuring such a race condition never happens.
- postDelayed(new Runnable() {
- @Override
- public void run() {
- updateSwitchImeButton();
- }
- }, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
}
@Override
@@ -265,59 +129,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
mPasswordEntryDisabler.setInputEnabled(enabled);
}
- /**
- * Method adapted from com.android.inputmethod.latin.Utils
- *
- * @param imm The input method manager
- * @param shouldIncludeAuxiliarySubtypes
- * @return true if we have multiple IMEs to choose from
- */
- private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
- final boolean shouldIncludeAuxiliarySubtypes) {
- final List<InputMethodInfo> enabledImis =
- imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser());
-
- // Number of the filtered IMEs
- int filteredImisCount = 0;
-
- for (InputMethodInfo imi : enabledImis) {
- // We can return true immediately after we find two or more filtered IMEs.
- if (filteredImisCount > 1) return true;
- final List<InputMethodSubtype> subtypes =
- imm.getEnabledInputMethodSubtypeList(imi, true);
- // IMEs that have no subtypes should be counted.
- if (subtypes.isEmpty()) {
- ++filteredImisCount;
- continue;
- }
-
- int auxCount = 0;
- for (InputMethodSubtype subtype : subtypes) {
- if (subtype.isAuxiliary()) {
- ++auxCount;
- }
- }
- final int nonAuxCount = subtypes.size() - auxCount;
-
- // IMEs that have one or more non-auxiliary subtypes should be counted.
- // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
- // subtypes should be counted as well.
- if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
- ++filteredImisCount;
- continue;
- }
- }
-
- return filteredImisCount > 1
- // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
- // input method subtype (The current IME should be LatinIME.)
- || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
@Override
public int getWrongPasswordStringId() {
return R.string.kg_wrong_password;
@@ -346,45 +157,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
}
@Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- if (mCallback != null) {
- mCallback.userActivity();
- }
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- // Poor man's user edit detection, assuming empty text is programmatic and everything else
- // is from the user.
- if (!TextUtils.isEmpty(s)) {
- onUserInput();
- }
- }
-
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- // Check if this was the result of hitting the enter key
- final boolean isSoftImeEvent = event == null
- && (actionId == EditorInfo.IME_NULL
- || actionId == EditorInfo.IME_ACTION_DONE
- || actionId == EditorInfo.IME_ACTION_NEXT);
- final boolean isKeyboardEnterKey = event != null
- && KeyEvent.isConfirmKey(event.getKeyCode())
- && event.getAction() == KeyEvent.ACTION_DOWN;
- if (isSoftImeEvent || isKeyboardEnterKey) {
- verifyPasswordAndUnlock();
- return true;
- }
- return false;
- }
-
- @Override
public CharSequence getTitle() {
- return getContext().getString(
+ return getResources().getString(
com.android.internal.R.string.keyguard_accessibility_password_unlock);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
new file mode 100644
index 000000000000..d34ea8c5e018
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -0,0 +1,275 @@
+/*
+ * 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.keyguard;
+
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.text.method.TextKeyListener;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import java.util.List;
+
+public class KeyguardPasswordViewController
+ extends KeyguardAbsKeyInputViewController<KeyguardPasswordView> {
+
+ private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
+
+ private final KeyguardSecurityCallback mKeyguardSecurityCallback;
+ private final InputMethodManager mInputMethodManager;
+ private final DelayableExecutor mMainExecutor;
+ private final boolean mShowImeAtScreenOn;
+ private TextView mPasswordEntry;
+ private View mSwitchImeButton;
+
+ private final OnEditorActionListener mOnEditorActionListener = (v, actionId, event) -> {
+ // Check if this was the result of hitting the enter key
+ final boolean isSoftImeEvent = event == null
+ && (actionId == EditorInfo.IME_NULL
+ || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT);
+ final boolean isKeyboardEnterKey = event != null
+ && KeyEvent.isConfirmKey(event.getKeyCode())
+ && event.getAction() == KeyEvent.ACTION_DOWN;
+ if (isSoftImeEvent || isKeyboardEnterKey) {
+ verifyPasswordAndUnlock();
+ return true;
+ }
+ return false;
+ };
+
+ private final TextWatcher mTextWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ mKeyguardSecurityCallback.userActivity();
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (!TextUtils.isEmpty(s)) {
+ onUserInput();
+ }
+ }
+ };
+
+ protected KeyguardPasswordViewController(KeyguardPasswordView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode,
+ LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ InputMethodManager inputMethodManager,
+ @Main DelayableExecutor mainExecutor,
+ @Main Resources resources) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker);
+ mKeyguardSecurityCallback = keyguardSecurityCallback;
+ mInputMethodManager = inputMethodManager;
+ mMainExecutor = mainExecutor;
+ mShowImeAtScreenOn = resources.getBoolean(R.bool.kg_show_ime_at_screen_on);
+ mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
+ mSwitchImeButton = mView.findViewById(R.id.switch_ime_button);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+ mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
+ mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+
+ // Set selected property on so the view can send accessibility events.
+ mPasswordEntry.setSelected(true);
+ mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener);
+ mPasswordEntry.addTextChangedListener(mTextWatcher);
+ // Poke the wakelock any time the text is selected or modified
+ mPasswordEntry.setOnClickListener(v -> mKeyguardSecurityCallback.userActivity());
+
+ mSwitchImeButton.setOnClickListener(v -> {
+ mKeyguardSecurityCallback.userActivity(); // Leave the screen on a bit longer
+ // Do not show auxiliary subtypes in password lock screen.
+ mInputMethodManager.showInputMethodPickerFromSystem(false,
+ mView.getContext().getDisplayId());
+ });
+
+ View cancelBtn = mView.findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(view -> {
+ mKeyguardSecurityCallback.reset();
+ mKeyguardSecurityCallback.onCancelClicked();
+ });
+ }
+
+ // If there's more than one IME, enable the IME switcher button
+ updateSwitchImeButton();
+
+ // When we the current user is switching, InputMethodManagerService sometimes has not
+ // switched internal state yet here. As a quick workaround, we check the keyboard state
+ // again.
+ // TODO: Remove this workaround by ensuring such a race condition never happens.
+ mMainExecutor.executeDelayed(
+ this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mPasswordEntry.setOnEditorActionListener(null);
+ }
+
+ @Override
+ public boolean needsInput() {
+ return true;
+ }
+
+ @Override
+ void resetState() {
+ mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ mMessageAreaController.setMessage("");
+ final boolean wasDisabled = mPasswordEntry.isEnabled();
+ mView.setPasswordEntryEnabled(true);
+ mView.setPasswordEntryInputEnabled(true);
+ // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage.
+ if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
+ return;
+ }
+ if (wasDisabled) {
+ mInputMethodManager.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+ }
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ // Wait a bit to focus the field so the focusable flag on the window is already set then.
+ mMainExecutor.execute(() -> {
+ if (mView.isShown() && mPasswordEntry.isEnabled()) {
+ mPasswordEntry.requestFocus();
+ if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
+ mInputMethodManager.showSoftInput(
+ mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
+ }
+
+ @Override
+ public void onStartingToHide() {
+ mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
+ }
+
+ private void updateSwitchImeButton() {
+ // If there's more than one IME, enable the IME switcher button
+ final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
+ final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(
+ mInputMethodManager, false);
+ if (wasVisible != shouldBeVisible) {
+ mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
+ }
+
+ // TODO: Check if we still need this hack.
+ // If no icon is visible, reset the start margin on the password field so the text is
+ // still centered.
+ if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
+ android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
+ if (params instanceof MarginLayoutParams) {
+ final MarginLayoutParams mlp = (MarginLayoutParams) params;
+ mlp.setMarginStart(0);
+ mPasswordEntry.setLayoutParams(params);
+ }
+ }
+ }
+
+ /**
+ * Method adapted from com.android.inputmethod.latin.Utils
+ *
+ * @param imm The input method manager
+ * @param shouldIncludeAuxiliarySubtypes
+ * @return true if we have multiple IMEs to choose from
+ */
+ private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
+ final boolean shouldIncludeAuxiliarySubtypes) {
+ final List<InputMethodInfo> enabledImis =
+ imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser());
+
+ // Number of the filtered IMEs
+ int filteredImisCount = 0;
+
+ for (InputMethodInfo imi : enabledImis) {
+ // We can return true immediately after we find two or more filtered IMEs.
+ if (filteredImisCount > 1) return true;
+ final List<InputMethodSubtype> subtypes =
+ imm.getEnabledInputMethodSubtypeList(imi, true);
+ // IMEs that have no subtypes should be counted.
+ if (subtypes.isEmpty()) {
+ ++filteredImisCount;
+ continue;
+ }
+
+ int auxCount = 0;
+ for (InputMethodSubtype subtype : subtypes) {
+ if (subtype.isAuxiliary()) {
+ ++auxCount;
+ }
+ }
+ final int nonAuxCount = subtypes.size() - auxCount;
+
+ // IMEs that have one or more non-auxiliary subtypes should be counted.
+ // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
+ // subtypes should be counted as well.
+ if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
+ ++filteredImisCount;
+ continue;
+ }
+ }
+
+ return filteredImisCount > 1
+ // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's
+ //enabled input method subtype (The current IME should be LatinIME.)
+ || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index c4a9fcb45284..bdcf467c2456 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,62 +15,39 @@
*/
package com.android.keyguard;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
-
import android.content.Context;
-import android.content.res.ColorStateList;
import android.graphics.Rect;
-import android.os.AsyncTask;
-import android.os.CountDownTimer;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.widget.LinearLayout;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
-import com.android.internal.widget.LockscreenCredential;
import com.android.settingslib.animation.AppearAnimationCreator;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import java.util.List;
-
-public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView,
- AppearAnimationCreator<LockPatternView.CellState>,
- EmergencyButton.EmergencyButtonCallback {
+public class KeyguardPatternView extends KeyguardInputView
+ implements AppearAnimationCreator<LockPatternView.CellState> {
private static final String TAG = "SecurityPatternView";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
- // how long before we clear the wrong pattern
- private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
// how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK
private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000;
- // how many cells the user has to cross before we poke the wakelock
- private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
-
// How much we scale up the duration of the disappear animation when the current user is locked
public static final float DISAPPEAR_MULTIPLIER_LOCKED = 1.5f;
// Extra padding, in pixels, that should eat touch events.
private static final int PATTERNS_TOUCH_AREA_EXTENSION = 40;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final AppearAnimationUtils mAppearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
@@ -78,11 +55,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
private final Rect mTempRect = new Rect();
private final Rect mLockPatternScreenBounds = new Rect();
- private CountDownTimer mCountdownTimer = null;
- private LockPatternUtils mLockPatternUtils;
- private AsyncTask<?, ?, ?> mPendingLockCheck;
private LockPatternView mLockPatternView;
- private KeyguardSecurityCallback mCallback;
/**
* Keeps track of the last time we poked the wake lock during dispatching of the touch event.
@@ -92,26 +65,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
*/
private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
- /**
- * Useful for clearing out the wrong pattern after a delay
- */
- private Runnable mCancelPatternRunnable = new Runnable() {
- @Override
- public void run() {
- mLockPatternView.clearPattern();
- }
- };
- @VisibleForTesting
KeyguardMessageArea mSecurityMessageDisplay;
private View mEcaView;
private ViewGroup mContainer;
- private int mDisappearYTranslation;
-
- enum FooterMode {
- Normal,
- ForgotLockPattern,
- VerifyUnlocked
- }
public KeyguardPatternView(Context context) {
this(context, null);
@@ -119,7 +75,6 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
public KeyguardPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mAppearAnimationUtils = new AppearAnimationUtils(context,
AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 1.5f /* translationScale */,
2.0f /* delayScale */, AnimationUtils.loadInterpolator(
@@ -132,50 +87,16 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
(long) (125 * DISAPPEAR_MULTIPLIER_LOCKED), 1.2f /* translationScale */,
0.6f /* delayScale */, AnimationUtils.loadInterpolator(
mContext, android.R.interpolator.fast_out_linear_in));
- mDisappearYTranslation = getResources().getDimensionPixelSize(
- R.dimen.disappear_y_translation);
- }
-
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mCallback = callback;
- }
-
- @Override
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mLockPatternUtils = mLockPatternUtils == null
- ? new LockPatternUtils(mContext) : mLockPatternUtils;
mLockPatternView = findViewById(R.id.lockPatternView);
- mLockPatternView.setSaveEnabled(false);
- mLockPatternView.setOnPatternListener(new UnlockPatternListener());
- mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
-
- // vibrate mode will be the same for the life of this screen
- mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
mContainer = findViewById(R.id.container);
-
- EmergencyButton button = findViewById(R.id.emergency_call_button);
- if (button != null) {
- button.setCallback(this);
- }
-
- View cancelBtn = findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(view -> {
- mCallback.reset();
- mCallback.onCancelClicked();
- });
- }
}
@Override
@@ -185,11 +106,6 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
}
@Override
- public void onEmergencyButtonClickedWhenInCall() {
- mCallback.reset();
- }
-
- @Override
public boolean onTouchEvent(MotionEvent ev) {
boolean result = super.onTouchEvent(ev);
// as long as the user is entering a pattern (i.e sending a touch event that was handled
@@ -217,248 +133,11 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
}
@Override
- public void reset() {
- // reset lock pattern
- mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
- mLockPatternView.enableInput();
- mLockPatternView.setEnabled(true);
- mLockPatternView.clearPattern();
-
- if (mSecurityMessageDisplay == null) {
- return;
- }
-
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
- if (deadline != 0) {
- handleAttemptLockout(deadline);
- } else {
- displayDefaultSecurityMessage();
- }
- }
-
- private void displayDefaultSecurityMessage() {
- if (mSecurityMessageDisplay != null) {
- mSecurityMessageDisplay.setMessage("");
- }
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
- @Override
- public boolean disallowInterceptTouch(MotionEvent event) {
+ boolean disallowInterceptTouch(MotionEvent event) {
return !mLockPatternView.isEmpty()
|| mLockPatternScreenBounds.contains((int) event.getRawX(), (int) event.getRawY());
}
- /** TODO: hook this up */
- public void cleanUp() {
- if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
- mLockPatternUtils = null;
- mLockPatternView.setOnPatternListener(null);
- }
-
- private class UnlockPatternListener implements LockPatternView.OnPatternListener {
-
- @Override
- public void onPatternStart() {
- mLockPatternView.removeCallbacks(mCancelPatternRunnable);
- mSecurityMessageDisplay.setMessage("");
- }
-
- @Override
- public void onPatternCleared() {
- }
-
- @Override
- public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
- mCallback.userActivity();
- mCallback.onUserInput();
- }
-
- @Override
- public void onPatternDetected(final List<LockPatternView.Cell> pattern) {
- mKeyguardUpdateMonitor.setCredentialAttempted();
- mLockPatternView.disableInput();
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- }
-
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
- if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
- mLockPatternView.enableInput();
- onPatternChecked(userId, false, 0, false /* not valid - too short */);
- return;
- }
-
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
- LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- mPendingLockCheck = LockPatternChecker.checkCredential(
- mLockPatternUtils,
- LockscreenCredential.createPattern(pattern),
- userId,
- new LockPatternChecker.OnCheckCallback() {
-
- @Override
- public void onEarlyMatched() {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL);
- }
- onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */,
- true /* isValidPattern */);
- }
-
- @Override
- public void onChecked(boolean matched, int timeoutMs) {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- mLockPatternView.enableInput();
- mPendingLockCheck = null;
- if (!matched) {
- onPatternChecked(userId, false /* matched */, timeoutMs,
- true /* isValidPattern */);
- }
- }
-
- @Override
- public void onCancelled() {
- // We already got dismissed with the early matched callback, so we
- // cancelled the check. However, we still need to note down the latency.
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- }
- });
- if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
- mCallback.userActivity();
- mCallback.onUserInput();
- }
- }
-
- private void onPatternChecked(int userId, boolean matched, int timeoutMs,
- boolean isValidPattern) {
- boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
- if (matched) {
- mCallback.reportUnlockAttempt(userId, true, 0);
- if (dismissKeyguard) {
- mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
- mCallback.dismiss(true, userId);
- }
- } else {
- mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
- if (isValidPattern) {
- mCallback.reportUnlockAttempt(userId, false, timeoutMs);
- if (timeoutMs > 0) {
- long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
- userId, timeoutMs);
- handleAttemptLockout(deadline);
- }
- }
- if (timeoutMs == 0) {
- mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern);
- mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
- }
- }
- }
- }
-
- private void handleAttemptLockout(long elapsedRealtimeDeadline) {
- mLockPatternView.clearPattern();
- mLockPatternView.setEnabled(false);
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- final long secondsInFuture = (long) Math.ceil(
- (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
- mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
-
- @Override
- public void onTick(long millisUntilFinished) {
- final int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
- mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString(
- R.plurals.kg_too_many_failed_attempts_countdown,
- secondsRemaining, secondsRemaining));
- }
-
- @Override
- public void onFinish() {
- mLockPatternView.setEnabled(true);
- displayDefaultSecurityMessage();
- }
-
- }.start();
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public void onPause() {
- if (mCountdownTimer != null) {
- mCountdownTimer.cancel();
- mCountdownTimer = null;
- }
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- mPendingLockCheck = null;
- }
- displayDefaultSecurityMessage();
- }
-
- @Override
- public void onResume(int reason) {
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
- @Override
- public void showPromptReason(int reason) {
- switch (reason) {
- case PROMPT_REASON_RESTART:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_restart_pattern);
- break;
- case PROMPT_REASON_TIMEOUT:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
- case PROMPT_REASON_DEVICE_ADMIN:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_device_admin);
- break;
- case PROMPT_REASON_USER_REQUEST:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_user_request);
- break;
- case PROMPT_REASON_PREPARE_FOR_UPDATE:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
- case PROMPT_REASON_NONE:
- break;
- default:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
- }
- }
-
- @Override
- public void showMessage(CharSequence message, ColorStateList colorState) {
- if (colorState != null) {
- mSecurityMessageDisplay.setNextMessageColor(colorState);
- }
- mSecurityMessageDisplay.setMessage(message);
- }
-
- @Override
public void startAppearAnimation() {
enableClipping(false);
setAlpha(1f);
@@ -467,12 +146,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
0, mAppearAnimationUtils.getInterpolator());
mAppearAnimationUtils.startAnimation2d(
mLockPatternView.getCellStates(),
- new Runnable() {
- @Override
- public void run() {
- enableClipping(true);
- }
- },
+ () -> enableClipping(true),
this);
if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
mAppearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
@@ -484,11 +158,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
}
}
- @Override
- public boolean startDisappearAnimation(final Runnable finishRunnable) {
- float durationMultiplier = mKeyguardUpdateMonitor.needsSlowUnlockTransition()
- ? DISAPPEAR_MULTIPLIER_LOCKED
- : 1f;
+ public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
+ final Runnable finishRunnable) {
+ float durationMultiplier = needsSlowUnlockTransition ? DISAPPEAR_MULTIPLIER_LOCKED : 1f;
mLockPatternView.clearPattern();
enableClipping(false);
setTranslationY(0);
@@ -497,10 +169,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
-mDisappearAnimationUtils.getStartTranslation(),
mDisappearAnimationUtils.getInterpolator());
- DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor
- .needsSlowUnlockTransition()
- ? mDisappearAnimationUtilsLocked
- : mDisappearAnimationUtils;
+ DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
+ ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils;
disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(),
() -> {
enableClipping(true);
@@ -549,7 +219,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
@Override
public CharSequence getTitle() {
- return getContext().getString(
+ return getResources().getString(
com.android.internal.R.string.keyguard_accessibility_pattern_unlock);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
new file mode 100644
index 000000000000..3db9db7be00c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -0,0 +1,349 @@
+/*
+ * 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.keyguard;
+
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+
+import android.content.res.ColorStateList;
+import android.os.AsyncTask;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.view.View;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockPatternView.Cell;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.keyguard.EmergencyButton.EmergencyButtonCallback;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+import java.util.List;
+
+public class KeyguardPatternViewController
+ extends KeyguardInputViewController<KeyguardPatternView> {
+
+ // how many cells the user has to cross before we poke the wakelock
+ private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
+
+ // how long before we clear the wrong pattern
+ private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
+
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final LockPatternUtils mLockPatternUtils;
+ private final LatencyTracker mLatencyTracker;
+ private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
+
+ private KeyguardMessageAreaController mMessageAreaController;
+ private LockPatternView mLockPatternView;
+ private CountDownTimer mCountdownTimer;
+ private AsyncTask<?, ?, ?> mPendingLockCheck;
+
+ private EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
+ @Override
+ public void onEmergencyButtonClickedWhenInCall() {
+ getKeyguardSecurityCallback().reset();
+ }
+ };
+
+ /**
+ * Useful for clearing out the wrong pattern after a delay
+ */
+ private Runnable mCancelPatternRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLockPatternView.clearPattern();
+ }
+ };
+
+ private class UnlockPatternListener implements LockPatternView.OnPatternListener {
+
+ @Override
+ public void onPatternStart() {
+ mLockPatternView.removeCallbacks(mCancelPatternRunnable);
+ mMessageAreaController.setMessage("");
+ }
+
+ @Override
+ public void onPatternCleared() {
+ }
+
+ @Override
+ public void onPatternCellAdded(List<Cell> pattern) {
+ getKeyguardSecurityCallback().userActivity();
+ getKeyguardSecurityCallback().onUserInput();
+ }
+
+ @Override
+ public void onPatternDetected(final List<LockPatternView.Cell> pattern) {
+ mKeyguardUpdateMonitor.setCredentialAttempted();
+ mLockPatternView.disableInput();
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ }
+
+ final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
+ mLockPatternView.enableInput();
+ onPatternChecked(userId, false, 0, false /* not valid - too short */);
+ return;
+ }
+
+ mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL);
+ mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ mPendingLockCheck = LockPatternChecker.checkCredential(
+ mLockPatternUtils,
+ LockscreenCredential.createPattern(pattern),
+ userId,
+ new LockPatternChecker.OnCheckCallback() {
+
+ @Override
+ public void onEarlyMatched() {
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL);
+ onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */,
+ true /* isValidPattern */);
+ }
+
+ @Override
+ public void onChecked(boolean matched, int timeoutMs) {
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ mLockPatternView.enableInput();
+ mPendingLockCheck = null;
+ if (!matched) {
+ onPatternChecked(userId, false /* matched */, timeoutMs,
+ true /* isValidPattern */);
+ }
+ }
+
+ @Override
+ public void onCancelled() {
+ // We already got dismissed with the early matched callback, so we
+ // cancelled the check. However, we still need to note down the latency.
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ }
+ });
+ if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
+ getKeyguardSecurityCallback().userActivity();
+ getKeyguardSecurityCallback().onUserInput();
+ }
+ }
+
+ private void onPatternChecked(int userId, boolean matched, int timeoutMs,
+ boolean isValidPattern) {
+ boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
+ if (matched) {
+ getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
+ if (dismissKeyguard) {
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
+ getKeyguardSecurityCallback().dismiss(true, userId);
+ }
+ } else {
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
+ if (isValidPattern) {
+ getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
+ if (timeoutMs > 0) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
+ userId, timeoutMs);
+ handleAttemptLockout(deadline);
+ }
+ }
+ if (timeoutMs == 0) {
+ mMessageAreaController.setMessage(R.string.kg_wrong_pattern);
+ mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
+ }
+ }
+ }
+ }
+
+ protected KeyguardPatternViewController(KeyguardPatternView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode,
+ LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ LatencyTracker latencyTracker,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
+ super(view, securityMode, keyguardSecurityCallback);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
+ mLatencyTracker = latencyTracker;
+ mMessageAreaControllerFactory = messageAreaControllerFactory;
+ KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
+ mMessageAreaController = mMessageAreaControllerFactory.create(kma);
+ mLockPatternView = mView.findViewById(R.id.lockPatternView);
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ mMessageAreaController.init();
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mLockPatternView.setOnPatternListener(new UnlockPatternListener());
+ mLockPatternView.setSaveEnabled(false);
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
+ KeyguardUpdateMonitor.getCurrentUser()));
+ // vibrate mode will be the same for the life of this screen
+ mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+
+ EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
+ if (button != null) {
+ button.setCallback(mEmergencyButtonCallback);
+ }
+
+ View cancelBtn = mView.findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(view -> {
+ getKeyguardSecurityCallback().reset();
+ getKeyguardSecurityCallback().onCancelClicked();
+ });
+ }
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mLockPatternView.setOnPatternListener(null);
+ EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
+ if (button != null) {
+ button.setCallback(null);
+ }
+ View cancelBtn = mView.findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(null);
+ }
+ }
+
+ @Override
+ public void reset() {
+ // reset lock pattern
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
+ KeyguardUpdateMonitor.getCurrentUser()));
+ mLockPatternView.enableInput();
+ mLockPatternView.setEnabled(true);
+ mLockPatternView.clearPattern();
+
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (deadline != 0) {
+ handleAttemptLockout(deadline);
+ } else {
+ displayDefaultSecurityMessage();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ if (mCountdownTimer != null) {
+ mCountdownTimer.cancel();
+ mCountdownTimer = null;
+ }
+
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ mPendingLockCheck = null;
+ }
+ displayDefaultSecurityMessage();
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ /// TODO: move all this logic into the MessageAreaController?
+ switch (reason) {
+ case PROMPT_REASON_RESTART:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_restart_pattern);
+ break;
+ case PROMPT_REASON_TIMEOUT:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
+ case PROMPT_REASON_DEVICE_ADMIN:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_device_admin);
+ break;
+ case PROMPT_REASON_USER_REQUEST:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_user_request);
+ break;
+ case PROMPT_REASON_PREPARE_FOR_UPDATE:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
+ case PROMPT_REASON_NONE:
+ break;
+ default:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
+ }
+ }
+
+ @Override
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (colorState != null) {
+ mMessageAreaController.setNextMessageColor(colorState);
+ }
+ mMessageAreaController.setMessage(message);
+ }
+
+ @Override
+ public void startAppearAnimation() {
+ super.startAppearAnimation();
+ }
+
+ @Override
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return mView.startDisappearAnimation(
+ mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
+ }
+
+ private void displayDefaultSecurityMessage() {
+ mMessageAreaController.setMessage("");
+ }
+
+ private void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ mLockPatternView.clearPattern();
+ mLockPatternView.setEnabled(false);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long secondsInFuture = (long) Math.ceil(
+ (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
+ mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ final int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
+ mMessageAreaController.setMessage(mView.getResources().getQuantityString(
+ R.plurals.kg_too_many_failed_attempts_countdown,
+ secondsRemaining, secondsRemaining));
+ }
+
+ @Override
+ public void onFinish() {
+ mLockPatternView.setEnabled(true);
+ displayDefaultSecurityMessage();
+ }
+
+ }.start();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index c7f27cf8a71a..7fa43116a7b1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -16,11 +16,17 @@
package com.android.keyguard;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.KeyEvent;
-import android.view.MotionEvent;
import android.view.View;
import com.android.internal.widget.LockscreenCredential;
@@ -29,22 +35,12 @@ import com.android.systemui.R;
/**
* A Pin based Keyguard input view
*/
-public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
- implements View.OnKeyListener, View.OnTouchListener {
+public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView {
protected PasswordTextView mPasswordEntry;
private View mOkButton;
private View mDeleteButton;
- private View mButton0;
- private View mButton1;
- private View mButton2;
- private View mButton3;
- private View mButton4;
- private View mButton5;
- private View mButton6;
- private View mButton7;
- private View mButton8;
- private View mButton9;
+ private View[] mButtons = new View[10];
public KeyguardPinBasedInputView(Context context) {
this(context, null);
@@ -62,7 +58,6 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
@Override
protected void resetState() {
- setPasswordEntryEnabled(true);
}
@Override
@@ -86,10 +81,10 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.isConfirmKey(keyCode)) {
- performClick(mOkButton);
+ mOkButton.performClick();
return true;
} else if (keyCode == KeyEvent.KEYCODE_DEL) {
- performClick(mDeleteButton);
+ mDeleteButton.performClick();
return true;
}
if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
@@ -125,42 +120,9 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
}
}
- private void performClick(View view) {
- view.performClick();
- }
-
private void performNumberClick(int number) {
- switch (number) {
- case 0:
- performClick(mButton0);
- break;
- case 1:
- performClick(mButton1);
- break;
- case 2:
- performClick(mButton2);
- break;
- case 3:
- performClick(mButton3);
- break;
- case 4:
- performClick(mButton4);
- break;
- case 5:
- performClick(mButton5);
- break;
- case 6:
- performClick(mButton6);
- break;
- case 7:
- performClick(mButton7);
- break;
- case 8:
- performClick(mButton8);
- break;
- case 9:
- performClick(mButton9);
- break;
+ if (number >= 0 && number <= 9) {
+ mButtons[number].performClick();
}
}
@@ -177,94 +139,31 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
@Override
protected void onFinishInflate() {
mPasswordEntry = findViewById(getPasswordTextViewId());
- mPasswordEntry.setOnKeyListener(this);
// Set selected property on so the view can send accessibility events.
mPasswordEntry.setSelected(true);
- mPasswordEntry.setUserActivityListener(new PasswordTextView.UserActivityListener() {
- @Override
- public void onUserActivity() {
- onUserInput();
- }
- });
-
mOkButton = findViewById(R.id.key_enter);
- if (mOkButton != null) {
- mOkButton.setOnTouchListener(this);
- mOkButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mPasswordEntry.isEnabled()) {
- verifyPasswordAndUnlock();
- }
- }
- });
- mOkButton.setOnHoverListener(new LiftToActivateListener(getContext()));
- }
mDeleteButton = findViewById(R.id.delete_button);
mDeleteButton.setVisibility(View.VISIBLE);
- mDeleteButton.setOnTouchListener(this);
- mDeleteButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- // check for time-based lockouts
- if (mPasswordEntry.isEnabled()) {
- mPasswordEntry.deleteLastChar();
- }
- }
- });
- mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- // check for time-based lockouts
- if (mPasswordEntry.isEnabled()) {
- resetPasswordText(true /* animate */, true /* announce */);
- }
- doHapticKeyClick();
- return true;
- }
- });
- mButton0 = findViewById(R.id.key0);
- mButton1 = findViewById(R.id.key1);
- mButton2 = findViewById(R.id.key2);
- mButton3 = findViewById(R.id.key3);
- mButton4 = findViewById(R.id.key4);
- mButton5 = findViewById(R.id.key5);
- mButton6 = findViewById(R.id.key6);
- mButton7 = findViewById(R.id.key7);
- mButton8 = findViewById(R.id.key8);
- mButton9 = findViewById(R.id.key9);
+ mButtons[0] = findViewById(R.id.key0);
+ mButtons[1] = findViewById(R.id.key1);
+ mButtons[2] = findViewById(R.id.key2);
+ mButtons[3] = findViewById(R.id.key3);
+ mButtons[4] = findViewById(R.id.key4);
+ mButtons[5] = findViewById(R.id.key5);
+ mButtons[6] = findViewById(R.id.key6);
+ mButtons[7] = findViewById(R.id.key7);
+ mButtons[8] = findViewById(R.id.key8);
+ mButtons[9] = findViewById(R.id.key9);
mPasswordEntry.requestFocus();
super.onFinishInflate();
}
@Override
- public void onResume(int reason) {
- super.onResume(reason);
- mPasswordEntry.requestFocus();
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- doHapticKeyClick();
- }
- return false;
- }
-
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- return onKeyDown(keyCode, event);
- }
- return false;
- }
-
- @Override
public CharSequence getTitle() {
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_pin_unlock);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
new file mode 100644
index 000000000000..4d0ebfffbe04
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -0,0 +1,113 @@
+/*
+ * 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.keyguard;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnKeyListener;
+import android.view.View.OnTouchListener;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
+ extends KeyguardAbsKeyInputViewController<T> {
+
+ private final LiftToActivateListener mLiftToActivateListener;
+ protected PasswordTextView mPasswordEntry;
+
+ private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ return mView.onKeyDown(keyCode, event);
+ }
+ return false;
+ };
+
+ private final OnTouchListener mOnTouchListener = (v, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mView.doHapticKeyClick();
+ }
+ return false;
+ };
+
+ protected KeyguardPinBasedInputViewController(T view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode,
+ LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ LiftToActivateListener liftToActivateListener) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker);
+ mLiftToActivateListener = liftToActivateListener;
+ mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+
+ mPasswordEntry.setOnKeyListener(mOnKeyListener);
+ mPasswordEntry.setUserActivityListener(this::onUserInput);
+
+ View deleteButton = mView.findViewById(R.id.delete_button);
+ deleteButton.setOnTouchListener(mOnTouchListener);
+ deleteButton.setOnClickListener(v -> {
+ // check for time-based lockouts
+ if (mPasswordEntry.isEnabled()) {
+ mPasswordEntry.deleteLastChar();
+ }
+ });
+ deleteButton.setOnLongClickListener(v -> {
+ // check for time-based lockouts
+ if (mPasswordEntry.isEnabled()) {
+ mView.resetPasswordText(true /* animate */, true /* announce */);
+ }
+ mView.doHapticKeyClick();
+ return true;
+ });
+
+ View okButton = mView.findViewById(R.id.key_enter);
+ if (okButton != null) {
+ okButton.setOnTouchListener(mOnTouchListener);
+ okButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mPasswordEntry.isEnabled()) {
+ verifyPasswordAndUnlock();
+ }
+ }
+ });
+ okButton.setOnHoverListener(mLiftToActivateListener);
+ }
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ mPasswordEntry.requestFocus();
+ }
+
+ @Override
+ void resetState() {
+ mView.setPasswordEntryEnabled(true);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
new file mode 100644
index 000000000000..6769436be8ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.view.View;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+public class KeyguardPinViewController
+ extends KeyguardPinBasedInputViewController<KeyguardPINView> {
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+ protected KeyguardPinViewController(KeyguardPINView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode, LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ LiftToActivateListener liftToActivateListener) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+
+ View cancelBtn = mView.findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(view -> {
+ getKeyguardSecurityCallback().reset();
+ getKeyguardSecurityCallback().onCancelClicked();
+ });
+ }
+ }
+
+ @Override
+ void resetState() {
+ super.resetState();
+ mMessageAreaController.setMessage("");
+ }
+
+ @Override
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return mView.startDisappearAnimation(
+ mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 81d37a830f8f..b62ea6bc2ff6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -19,8 +19,6 @@ 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 com.android.systemui.DejankUtils.whitelistIpcs;
-
import static java.lang.Integer.max;
import android.animation.Animator;
@@ -28,25 +26,14 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.content.Intent;
-import android.content.res.ColorStateList;
import android.graphics.Insets;
import android.graphics.Rect;
-import android.metrics.LogMaker;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.MathUtils;
-import android.util.Slog;
import android.util.TypedValue;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
-import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
@@ -61,42 +48,30 @@ import androidx.annotation.VisibleForTesting;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLoggerImpl;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
-import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.InjectionInflationController;
import java.util.List;
-public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
- private static final boolean DEBUG = KeyguardConstants.DEBUG;
- private static final String TAG = "KeyguardSecurityView";
-
- private static final int USER_TYPE_PRIMARY = 1;
- private static final int USER_TYPE_WORK_PROFILE = 2;
- private static final int USER_TYPE_SECONDARY_USER = 3;
+public class KeyguardSecurityContainer extends FrameLayout {
+ static final int USER_TYPE_PRIMARY = 1;
+ static final int USER_TYPE_WORK_PROFILE = 2;
+ static final int USER_TYPE_SECONDARY_USER = 3;
// Bouncer is dismissed due to no security.
- private static final int BOUNCER_DISMISS_NONE_SECURITY = 0;
+ static final int BOUNCER_DISMISS_NONE_SECURITY = 0;
// Bouncer is dismissed due to pin, password or pattern entered.
- private static final int BOUNCER_DISMISS_PASSWORD = 1;
+ static final int BOUNCER_DISMISS_PASSWORD = 1;
// Bouncer is dismissed due to biometric (face, fingerprint or iris) authenticated.
- private static final int BOUNCER_DISMISS_BIOMETRIC = 2;
+ static final int BOUNCER_DISMISS_BIOMETRIC = 2;
// Bouncer is dismissed due to extended access granted.
- private static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3;
+ static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3;
// Bouncer is dismissed due to sim card unlock code entered.
- private static final int BOUNCER_DISMISS_SIM = 4;
+ static final int BOUNCER_DISMISS_SIM = 4;
// Make the view move slower than the finger, as if the spring were applying force.
private static final float TOUCH_Y_MULTIPLIER = 0.25f;
@@ -105,36 +80,23 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
// How much to scale the default slop by, to avoid accidental drags.
private static final float SLOP_SCALE = 4f;
- private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl();
-
private static final long IME_DISAPPEAR_DURATION_MS = 125;
- private KeyguardSecurityModel mSecurityModel;
- private LockPatternUtils mLockPatternUtils;
-
@VisibleForTesting
KeyguardSecurityViewFlipper mSecurityViewFlipper;
- private boolean mIsVerifyUnlockOnly;
- private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
- private KeyguardSecurityView mCurrentSecurityView;
- private SecurityCallback mSecurityCallback;
private AlertDialog mAlertDialog;
- private InjectionInflationController mInjectionInflationController;
private boolean mSwipeUpToRetry;
- private AdminSecondaryLockScreenController mSecondaryLockScreenController;
private final ViewConfiguration mViewConfiguration;
private final SpringAnimation mSpringAnimation;
private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
- private final KeyguardUpdateMonitor mUpdateMonitor;
- private final KeyguardStateController mKeyguardStateController;
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private float mLastTouchY = -1;
private int mActivePointerId = -1;
private boolean mIsDragging;
private float mStartTouchY = -1;
private boolean mDisappearAnimRunning;
+ private SwipeListener mSwipeListener;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -186,19 +148,22 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
- public boolean dismiss(boolean authenticated, int targetUserId,
- boolean bypassSecondaryLockScreen);
- public void userActivity();
- public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
+ boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
+ void userActivity();
+ void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
/**
* @param strongAuth wheher the user has authenticated with strong authentication like
* pattern, password or PIN but not by trust agents or fingerprint
* @param targetUserId a user that needs to be the foreground user at the finish completion.
*/
- public void finish(boolean strongAuth, int targetUserId);
- public void reset();
- public void onCancelClicked();
+ void finish(boolean strongAuth, int targetUserId);
+ void reset();
+ void onCancelClicked();
+ }
+
+ public interface SwipeListener {
+ void onSwipeUp();
}
@VisibleForTesting
@@ -249,52 +214,24 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mSecurityModel = Dependency.get(KeyguardSecurityModel.class);
- mLockPatternUtils = new LockPatternUtils(context);
- mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
- mInjectionInflationController = new InjectionInflationController(
- SystemUIFactory.getInstance().getSysUIComponent().createViewInstanceCreatorFactory());
mViewConfiguration = ViewConfiguration.get(context);
- mKeyguardStateController = Dependency.get(KeyguardStateController.class);
- mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this,
- mUpdateMonitor, mCallback, new Handler(Looper.myLooper()));
- }
-
- public void setSecurityCallback(SecurityCallback callback) {
- mSecurityCallback = callback;
}
- @Override
- public void onResume(int reason) {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).onResume(reason);
- }
+ void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
- updateBiometricRetry();
+ updateBiometricRetry(securityMode, faceAuthEnabled);
}
- @Override
public void onPause() {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
mAlertDialog = null;
}
- mSecondaryLockScreenController.hide();
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).onPause();
- }
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
}
@Override
- public void onStartingToHide() {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).onStartingToHide();
- }
- }
-
- @Override
public boolean shouldDelayChildPressedState() {
return true;
}
@@ -316,13 +253,12 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
return false;
}
// Avoid dragging the pattern view
- if (mCurrentSecurityView.disallowInterceptTouch(event)) {
+ if (mSecurityViewFlipper.getSecurityView().disallowInterceptTouch(event)) {
return false;
}
int index = event.findPointerIndex(mActivePointerId);
float touchSlop = mViewConfiguration.getScaledTouchSlop() * SLOP_SCALE;
- if (mCurrentSecurityView != null && index != -1
- && mStartTouchY - event.getY(index) > touchSlop) {
+ if (index != -1 && mStartTouchY - event.getY(index) > touchSlop) {
mIsDragging = true;
return true;
}
@@ -370,31 +306,28 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
}
if (action == MotionEvent.ACTION_UP) {
if (-getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- MIN_DRAG_SIZE, getResources().getDisplayMetrics())
- && !mUpdateMonitor.isFaceDetectionRunning()) {
- mUpdateMonitor.requestFaceAuth();
- mCallback.userActivity();
- showMessage(null, null);
+ MIN_DRAG_SIZE, getResources().getDisplayMetrics())) {
+ if (mSwipeListener != null) {
+ mSwipeListener.onSwipeUp();
+ }
}
}
return true;
}
+ void setSwipeListener(SwipeListener swipeListener) {
+ mSwipeListener = swipeListener;
+ }
+
private void startSpringAnimation(float startVelocity) {
mSpringAnimation
.setStartVelocity(startVelocity)
.animateToFinalPosition(0);
}
- public void startAppearAnimation() {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
- }
- }
-
- public boolean startDisappearAnimation(Runnable onFinishRunnable) {
+ public void startDisappearAnimation(SecurityMode securitySelection) {
mDisappearAnimRunning = true;
- if (mCurrentSecuritySelection == SecurityMode.Password) {
+ if (securitySelection == SecurityMode.Password) {
mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
IME_DISAPPEAR_DURATION_MS,
Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
@@ -439,19 +372,13 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
}
});
}
- if (mCurrentSecuritySelection != SecurityMode.None) {
- return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
- onFinishRunnable);
- }
- return false;
}
/**
* Enables/disables swipe up to retry on the bouncer.
*/
- private void updateBiometricRetry() {
- SecurityMode securityMode = getSecurityMode();
- mSwipeUpToRetry = mKeyguardStateController.isFaceAuthEnabled()
+ private void updateBiometricRetry(SecurityMode securityMode, boolean faceAuthEnabled) {
+ mSwipeUpToRetry = faceAuthEnabled
&& securityMode != SecurityMode.SimPin
&& securityMode != SecurityMode.SimPuk
&& securityMode != SecurityMode.None;
@@ -461,53 +388,11 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
return mSecurityViewFlipper.getTitle();
}
- @VisibleForTesting
- protected KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
- final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
- KeyguardSecurityView view = null;
- final int children = mSecurityViewFlipper.getChildCount();
- for (int child = 0; child < children; child++) {
- if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
- view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
- break;
- }
- }
- int layoutId = getLayoutIdFor(securityMode);
- if (view == null && layoutId != 0) {
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
- View v = mInjectionInflationController.injectable(inflater)
- .inflate(layoutId, mSecurityViewFlipper, false);
- mSecurityViewFlipper.addView(v);
- updateSecurityView(v);
- view = (KeyguardSecurityView)v;
- view.reset();
- }
-
- return view;
- }
-
- private void updateSecurityView(View view) {
- if (view instanceof KeyguardSecurityView) {
- KeyguardSecurityView ksv = (KeyguardSecurityView) view;
- ksv.setKeyguardCallback(mCallback);
- ksv.setLockPatternUtils(mLockPatternUtils);
- } else {
- Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
- }
- }
@Override
public void onFinishInflate() {
super.onFinishInflate();
mSecurityViewFlipper = findViewById(R.id.view_flipper);
- mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
- }
-
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
- mSecurityModel.setLockPatternUtils(utils);
- mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
}
@Override
@@ -539,11 +424,12 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
mAlertDialog.show();
}
- private void showTimeoutDialog(int userId, int timeoutMs) {
- int timeoutInSeconds = (int) timeoutMs / 1000;
+ void showTimeoutDialog(int userId, int timeoutMs, LockPatternUtils lockPatternUtils,
+ SecurityMode securityMode) {
+ int timeoutInSeconds = timeoutMs / 1000;
int messageId = 0;
- switch (mSecurityModel.getSecurityMode(userId)) {
+ switch (securityMode) {
case Pattern:
messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
break;
@@ -563,13 +449,13 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
if (messageId != 0) {
final String message = mContext.getString(messageId,
- mLockPatternUtils.getCurrentFailedPasswordAttempts(userId),
+ lockPatternUtils.getCurrentFailedPasswordAttempts(userId),
timeoutInSeconds);
showDialog(null, message);
}
}
- private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
+ void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
String message = null;
switch (userType) {
case USER_TYPE_PRIMARY:
@@ -588,7 +474,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
showDialog(null, message);
}
- private void showWipeDialog(int attempts, int userType) {
+ void showWipeDialog(int attempts, int userType) {
String message = null;
switch (userType) {
case USER_TYPE_PRIMARY:
@@ -607,358 +493,8 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
showDialog(null, message);
}
- private void reportFailedUnlockAttempt(int userId, int timeoutMs) {
- // +1 for this time
- final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
-
- if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
-
- final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
- final int failedAttemptsBeforeWipe =
- dpm.getMaximumFailedPasswordsForWipe(null, userId);
-
- final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
- (failedAttemptsBeforeWipe - failedAttempts)
- : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
- if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
- // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
- // N attempts. Once we get below the grace period, we post this dialog every time as a
- // clear warning until the deletion fires.
- // Check which profile has the strictest policy for failed password attempts
- final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
- int userType = USER_TYPE_PRIMARY;
- if (expiringUser == userId) {
- // TODO: http://b/23522538
- if (expiringUser != UserHandle.USER_SYSTEM) {
- userType = USER_TYPE_SECONDARY_USER;
- }
- } else if (expiringUser != UserHandle.USER_NULL) {
- userType = USER_TYPE_WORK_PROFILE;
- } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
- if (remainingBeforeWipe > 0) {
- showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
- } else {
- // Too many attempts. The device will be wiped shortly.
- Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
- showWipeDialog(failedAttempts, userType);
- }
- }
- mLockPatternUtils.reportFailedPasswordAttempt(userId);
- if (timeoutMs > 0) {
- mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
- showTimeoutDialog(userId, timeoutMs);
- }
- }
-
- /**
- * Shows the primary security screen for the user. This will be either the multi-selector
- * or the user's security method.
- * @param turningOff true if the device is being turned off
- */
- void showPrimarySecurityScreen(boolean turningOff) {
- SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser()));
- if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
- showSecurityScreen(securityMode);
- }
-
- /**
- * Shows the next security screen if there is one.
- * @param authenticated true if the user entered the correct authentication
- * @param targetUserId a user that needs to be the foreground user at the finish (if called)
- * completion.
- * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
- * secondary lock screen requirement, if any.
- * @return true if keyguard is done
- */
- boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
- boolean bypassSecondaryLockScreen) {
- if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
- boolean finish = false;
- boolean strongAuth = false;
- int eventSubtype = -1;
- BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN;
- if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
- finish = true;
- eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS;
- } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) {
- finish = true;
- eventSubtype = BOUNCER_DISMISS_BIOMETRIC;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC;
- } else if (SecurityMode.None == mCurrentSecuritySelection) {
- SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
- if (SecurityMode.None == securityMode) {
- finish = true; // no security required
- eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY;
- } else {
- showSecurityScreen(securityMode); // switch to the alternate security view
- }
- } else if (authenticated) {
- switch (mCurrentSecuritySelection) {
- case Pattern:
- case Password:
- case PIN:
- strongAuth = true;
- finish = true;
- eventSubtype = BOUNCER_DISMISS_PASSWORD;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD;
- break;
-
- case SimPin:
- case SimPuk:
- // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
- SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
- if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser())) {
- finish = true;
- eventSubtype = BOUNCER_DISMISS_SIM;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM;
- } else {
- showSecurityScreen(securityMode);
- }
- break;
-
- default:
- Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
- showPrimarySecurityScreen(false);
- break;
- }
- }
- // Check for device admin specified additional security measures.
- if (finish && !bypassSecondaryLockScreen) {
- Intent secondaryLockscreenIntent =
- mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
- if (secondaryLockscreenIntent != null) {
- mSecondaryLockScreenController.show(secondaryLockscreenIntent);
- return false;
- }
- }
- if (eventSubtype != -1) {
- mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
- .setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
- }
- if (uiEvent != BouncerUiEvent.UNKNOWN) {
- sUiEventLogger.log(uiEvent);
- }
- if (finish) {
- mSecurityCallback.finish(strongAuth, targetUserId);
- }
- return finish;
- }
-
- /**
- * Switches to the given security view unless it's already being shown, in which case
- * this is a no-op.
- *
- * @param securityMode
- */
- private void showSecurityScreen(SecurityMode securityMode) {
- if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
-
- if (securityMode == mCurrentSecuritySelection) return;
-
- KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
- KeyguardSecurityView newView = getSecurityView(securityMode);
-
- // Emulate Activity life cycle
- if (oldView != null) {
- oldView.onPause();
- oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
- }
- if (securityMode != SecurityMode.None) {
- newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
- newView.setKeyguardCallback(mCallback);
- }
-
- // Find and show this child.
- final int childCount = mSecurityViewFlipper.getChildCount();
-
- final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
- for (int i = 0; i < childCount; i++) {
- if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
- mSecurityViewFlipper.setDisplayedChild(i);
- break;
- }
- }
-
- mCurrentSecuritySelection = securityMode;
- mCurrentSecurityView = newView;
- mSecurityCallback.onSecurityModeChanged(securityMode,
- securityMode != SecurityMode.None && newView.needsInput());
- }
-
- private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
- public void userActivity() {
- if (mSecurityCallback != null) {
- mSecurityCallback.userActivity();
- }
- }
-
- @Override
- public void onUserInput() {
- mUpdateMonitor.cancelFaceAuth();
- }
-
- @Override
- public void dismiss(boolean authenticated, int targetId) {
- dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
- }
-
- @Override
- public void dismiss(boolean authenticated, int targetId,
- boolean bypassSecondaryLockScreen) {
- mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
- }
-
- public boolean isVerifyUnlockOnly() {
- return mIsVerifyUnlockOnly;
- }
-
- public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
- if (success) {
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
- SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
- mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
- // Force a garbage collection in an attempt to erase any lockscreen password left in
- // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
- // dismiss animation janky.
- ThreadUtils.postOnBackgroundThread(() -> {
- try {
- Thread.sleep(5000);
- } catch (InterruptedException ignored) { }
- Runtime.getRuntime().gc();
- });
- } else {
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
- SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
- KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
- }
- mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
- .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
- sUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS
- : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE);
- }
-
- public void reset() {
- mSecurityCallback.reset();
- }
-
- public void onCancelClicked() {
- mSecurityCallback.onCancelClicked();
- }
- };
-
- // The following is used to ignore callbacks from SecurityViews that are no longer current
- // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
- // state for the current security method.
- private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
- @Override
- public void userActivity() { }
- @Override
- public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
- @Override
- public boolean isVerifyUnlockOnly() { return false; }
- @Override
- public void dismiss(boolean securityVerified, int targetUserId) { }
- @Override
- public void dismiss(boolean authenticated, int targetId,
- boolean bypassSecondaryLockScreen) { }
- @Override
- public void onUserInput() { }
- @Override
- public void reset() {}
- };
-
- private int getSecurityViewIdForMode(SecurityMode securityMode) {
- switch (securityMode) {
- case Pattern: return R.id.keyguard_pattern_view;
- case PIN: return R.id.keyguard_pin_view;
- case Password: return R.id.keyguard_password_view;
- case SimPin: return R.id.keyguard_sim_pin_view;
- case SimPuk: return R.id.keyguard_sim_puk_view;
- }
- return 0;
- }
-
- @VisibleForTesting
- public int getLayoutIdFor(SecurityMode securityMode) {
- switch (securityMode) {
- case Pattern: return R.layout.keyguard_pattern_view;
- case PIN: return R.layout.keyguard_pin_view;
- case Password: return R.layout.keyguard_password_view;
- case SimPin: return R.layout.keyguard_sim_pin_view;
- case SimPuk: return R.layout.keyguard_sim_puk_view;
- default:
- return 0;
- }
- }
-
- public SecurityMode getSecurityMode() {
- return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser());
- }
-
- public SecurityMode getCurrentSecurityMode() {
- return mCurrentSecuritySelection;
- }
-
- public KeyguardSecurityView getCurrentSecurityView() {
- return mCurrentSecurityView;
- }
-
- public void verifyUnlock() {
- mIsVerifyUnlockOnly = true;
- showSecurityScreen(getSecurityMode());
- }
-
- public SecurityMode getCurrentSecuritySelection() {
- return mCurrentSecuritySelection;
- }
-
- public void dismiss(boolean authenticated, int targetUserId) {
- mCallback.dismiss(authenticated, targetUserId);
- }
-
- public boolean needsInput() {
- return mSecurityViewFlipper.needsInput();
- }
-
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mSecurityViewFlipper.setKeyguardCallback(callback);
- }
-
- @Override
public void reset() {
- mSecurityViewFlipper.reset();
mDisappearAnimRunning = false;
}
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mSecurityViewFlipper.getCallback();
- }
-
- @Override
- public void showPromptReason(int reason) {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- if (reason != PROMPT_REASON_NONE) {
- Log.i(TAG, "Strong auth required, reason: " + reason);
- }
- getSecurityView(mCurrentSecuritySelection).showPromptReason(reason);
- }
- }
-
- public void showMessage(CharSequence message, ColorStateList colorState) {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).showMessage(message, colorState);
- }
- }
-
- @Override
- public void showUsabilityHint() {
- mSecurityViewFlipper.showUsabilityHint();
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 17f25bd08ef4..1c23605a8516 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -16,33 +16,167 @@
package com.android.keyguard;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_NONE_SECURITY;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_PASSWORD;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_SIM;
+import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_PRIMARY;
+import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER;
+import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE;
+import static com.android.systemui.DejankUtils.whitelistIpcs;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Intent;
import android.content.res.ColorStateList;
+import android.metrics.LogMaker;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityContainer.BouncerUiEvent;
import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
+import com.android.keyguard.KeyguardSecurityContainer.SwipeListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
/** Controller for {@link KeyguardSecurityContainer} */
-public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer> {
+@KeyguardBouncerScope
+public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer>
+ implements KeyguardSecurityView {
+
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ private static final String TAG = "KeyguardSecurityView";
+ private final AdminSecondaryLockScreenController mAdminSecondaryLockScreenController;
private final LockPatternUtils mLockPatternUtils;
- private final KeyguardSecurityViewController.Factory mKeyguardSecurityViewControllerFactory;
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final KeyguardSecurityModel mSecurityModel;
+ private final MetricsLogger mMetricsLogger;
+ private final UiEventLogger mUiEventLogger;
+ private final KeyguardStateController mKeyguardStateController;
+ private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
+ private final SecurityCallback mSecurityCallback;
+
+ private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid;
+
+ private KeyguardSecurityCallback mKeyguardSecurityCallback = new KeyguardSecurityCallback() {
+ public void userActivity() {
+ if (mSecurityCallback != null) {
+ mSecurityCallback.userActivity();
+ }
+ }
+
+ @Override
+ public void onUserInput() {
+ mUpdateMonitor.cancelFaceAuth();
+ }
+
+ @Override
+ public void dismiss(boolean authenticated, int targetId) {
+ dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
+ }
+
+ @Override
+ public void dismiss(boolean authenticated, int targetId,
+ boolean bypassSecondaryLockScreen) {
+ mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
+ }
+
+ public boolean isVerifyUnlockOnly() {
+ return false;
+ }
+
+ public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
+ if (success) {
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
+ SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
+ mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
+ // Force a garbage collection in an attempt to erase any lockscreen password left in
+ // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
+ // dismiss animation janky.
+ ThreadUtils.postOnBackgroundThread(() -> {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException ignored) { }
+ Runtime.getRuntime().gc();
+ });
+ } else {
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
+ SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
+ reportFailedUnlockAttempt(userId, timeoutMs);
+ }
+ mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
+ .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
+ mUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS
+ : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE);
+ }
+
+ public void reset() {
+ mSecurityCallback.reset();
+ }
+
+ public void onCancelClicked() {
+ mSecurityCallback.onCancelClicked();
+ }
+ };
+
- @Inject
- KeyguardSecurityContainerController(KeyguardSecurityContainer view,
+ private SwipeListener mSwipeListener = new SwipeListener() {
+ @Override
+ public void onSwipeUp() {
+ if (!mUpdateMonitor.isFaceDetectionRunning()) {
+ mUpdateMonitor.requestFaceAuth();
+ mKeyguardSecurityCallback.userActivity();
+ showMessage(null, null);
+ }
+ }
+ };
+
+ private KeyguardSecurityContainerController(KeyguardSecurityContainer view,
+ AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory,
LockPatternUtils lockPatternUtils,
- KeyguardSecurityViewController.Factory keyguardSecurityViewControllerFactory) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardSecurityModel keyguardSecurityModel,
+ MetricsLogger metricsLogger,
+ UiEventLogger uiEventLogger,
+ KeyguardStateController keyguardStateController,
+ SecurityCallback securityCallback,
+ KeyguardSecurityViewFlipperController securityViewFlipperController) {
super(view);
mLockPatternUtils = lockPatternUtils;
- view.setLockPatternUtils(mLockPatternUtils);
- mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
+ mUpdateMonitor = keyguardUpdateMonitor;
+ mSecurityModel = keyguardSecurityModel;
+ mMetricsLogger = metricsLogger;
+ mUiEventLogger = uiEventLogger;
+ mKeyguardStateController = keyguardStateController;
+ mSecurityCallback = securityCallback;
+ mSecurityViewFlipperController = securityViewFlipperController;
+ mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create(
+ mKeyguardSecurityCallback);
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ mSecurityViewFlipperController.init();
}
@Override
protected void onViewAttached() {
+ mView.setSwipeListener(mSwipeListener);
}
@Override
@@ -51,68 +185,311 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
/** */
public void onPause() {
+ mAdminSecondaryLockScreenController.hide();
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().onPause();
+ }
mView.onPause();
}
+
+ /**
+ * Shows the primary security screen for the user. This will be either the multi-selector
+ * or the user's security method.
+ * @param turningOff true if the device is being turned off
+ */
public void showPrimarySecurityScreen(boolean turningOff) {
- mView.showPrimarySecurityScreen(turningOff);
+ SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode(
+ KeyguardUpdateMonitor.getCurrentUser()));
+ if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
+ showSecurityScreen(securityMode);
}
+ @Override
public void showPromptReason(int reason) {
- mView.showPromptReason(reason);
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ if (reason != PROMPT_REASON_NONE) {
+ Log.i(TAG, "Strong auth required, reason: " + reason);
+ }
+ getCurrentSecurityController().showPromptReason(reason);
+ }
}
public void showMessage(CharSequence message, ColorStateList colorState) {
- mView.showMessage(message, colorState);
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().showMessage(message, colorState);
+ }
}
- public SecurityMode getCurrentSecuritySelection() {
- return mView.getCurrentSecuritySelection();
+ public SecurityMode getCurrentSecurityMode() {
+ return mCurrentSecurityMode;
}
public void dismiss(boolean authenticated, int targetUserId) {
- mView.dismiss(authenticated, targetUserId);
+ mKeyguardSecurityCallback.dismiss(authenticated, targetUserId);
}
public void reset() {
mView.reset();
+ mSecurityViewFlipperController.reset();
}
public CharSequence getTitle() {
return mView.getTitle();
}
- public void onResume(int screenOn) {
- mView.onResume(screenOn);
+ @Override
+ public void onResume(int reason) {
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().onResume(reason);
+ }
+ mView.onResume(
+ mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()),
+ mKeyguardStateController.isFaceAuthEnabled());
}
public void startAppearAnimation() {
- mView.startAppearAnimation();
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().startAppearAnimation();
+ }
}
public boolean startDisappearAnimation(Runnable onFinishRunnable) {
- return mView.startDisappearAnimation(onFinishRunnable);
- }
+ mView.startDisappearAnimation(getCurrentSecurityMode());
- public void onStartingToHide() {
- mView.onStartingToHide();
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ return getCurrentSecurityController().startDisappearAnimation(onFinishRunnable);
+ }
+
+ return false;
}
- public void setSecurityCallback(SecurityCallback securityCallback) {
- mView.setSecurityCallback(securityCallback);
+ public void onStartingToHide() {
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().onStartingToHide();
+ }
}
+ /**
+ * Shows the next security screen if there is one.
+ * @param authenticated true if the user entered the correct authentication
+ * @param targetUserId a user that needs to be the foreground user at the finish (if called)
+ * completion.
+ * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
+ * secondary lock screen requirement, if any.
+ * @return true if keyguard is done
+ */
public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
boolean bypassSecondaryLockScreen) {
- return mView.showNextSecurityScreenOrFinish(
- authenticated, targetUserId, bypassSecondaryLockScreen);
+
+ if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
+ boolean finish = false;
+ boolean strongAuth = false;
+ int eventSubtype = -1;
+ BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN;
+ if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS;
+ } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_BIOMETRIC;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC;
+ } else if (SecurityMode.None == getCurrentSecurityMode()) {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
+ if (SecurityMode.None == securityMode) {
+ finish = true; // no security required
+ eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY;
+ } else {
+ showSecurityScreen(securityMode); // switch to the alternate security view
+ }
+ } else if (authenticated) {
+ switch (getCurrentSecurityMode()) {
+ case Pattern:
+ case Password:
+ case PIN:
+ strongAuth = true;
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_PASSWORD;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD;
+ break;
+
+ case SimPin:
+ case SimPuk:
+ // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
+ SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
+ if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled(
+ KeyguardUpdateMonitor.getCurrentUser())) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_SIM;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM;
+ } else {
+ showSecurityScreen(securityMode);
+ }
+ break;
+
+ default:
+ Log.v(TAG, "Bad security screen " + getCurrentSecurityMode()
+ + ", fail safe");
+ showPrimarySecurityScreen(false);
+ break;
+ }
+ }
+ // Check for device admin specified additional security measures.
+ if (finish && !bypassSecondaryLockScreen) {
+ Intent secondaryLockscreenIntent =
+ mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
+ if (secondaryLockscreenIntent != null) {
+ mAdminSecondaryLockScreenController.show(secondaryLockscreenIntent);
+ return false;
+ }
+ }
+ if (eventSubtype != -1) {
+ mMetricsLogger.write(new LogMaker(MetricsProto.MetricsEvent.BOUNCER)
+ .setType(MetricsProto.MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
+ }
+ if (uiEvent != BouncerUiEvent.UNKNOWN) {
+ mUiEventLogger.log(uiEvent);
+ }
+ if (finish) {
+ mSecurityCallback.finish(strongAuth, targetUserId);
+ }
+ return finish;
}
public boolean needsInput() {
- return mView.needsInput();
+ return getCurrentSecurityController().needsInput();
}
- public SecurityMode getCurrentSecurityMode() {
- return mView.getCurrentSecurityMode();
+ /**
+ * Switches to the given security view unless it's already being shown, in which case
+ * this is a no-op.
+ *
+ * @param securityMode
+ */
+ @VisibleForTesting
+ void showSecurityScreen(SecurityMode securityMode) {
+ if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
+
+ if (securityMode == SecurityMode.Invalid || securityMode == mCurrentSecurityMode) {
+ return;
+ }
+
+ KeyguardInputViewController<KeyguardInputView> oldView = getCurrentSecurityController();
+
+ // Emulate Activity life cycle
+ if (oldView != null) {
+ oldView.onPause();
+ }
+
+ KeyguardInputViewController<KeyguardInputView> newView = changeSecurityMode(securityMode);
+ if (newView != null) {
+ newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
+ mSecurityViewFlipperController.show(newView);
+ }
+
+ mSecurityCallback.onSecurityModeChanged(
+ securityMode, newView != null && newView.needsInput());
+ }
+
+ public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
+ // +1 for this time
+ final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
+
+ if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
+
+ final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
+ final int failedAttemptsBeforeWipe =
+ dpm.getMaximumFailedPasswordsForWipe(null, userId);
+
+ final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0
+ ? (failedAttemptsBeforeWipe - failedAttempts)
+ : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
+ if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
+ // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
+ // N attempts. Once we get below the grace period, we post this dialog every time as a
+ // clear warning until the deletion fires.
+ // Check which profile has the strictest policy for failed password attempts
+ final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
+ int userType = USER_TYPE_PRIMARY;
+ if (expiringUser == userId) {
+ // TODO: http://b/23522538
+ if (expiringUser != UserHandle.USER_SYSTEM) {
+ userType = USER_TYPE_SECONDARY_USER;
+ }
+ } else if (expiringUser != UserHandle.USER_NULL) {
+ userType = USER_TYPE_WORK_PROFILE;
+ } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
+ if (remainingBeforeWipe > 0) {
+ mView.showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
+ } else {
+ // Too many attempts. The device will be wiped shortly.
+ Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
+ mView.showWipeDialog(failedAttempts, userType);
+ }
+ }
+ mLockPatternUtils.reportFailedPasswordAttempt(userId);
+ if (timeoutMs > 0) {
+ mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
+ mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils,
+ mSecurityModel.getSecurityMode(userId));
+ }
+ }
+
+ private KeyguardInputViewController<KeyguardInputView> getCurrentSecurityController() {
+ return mSecurityViewFlipperController
+ .getSecurityView(mCurrentSecurityMode, mKeyguardSecurityCallback);
+ }
+
+ private KeyguardInputViewController<KeyguardInputView> changeSecurityMode(
+ SecurityMode securityMode) {
+ mCurrentSecurityMode = securityMode;
+ return getCurrentSecurityController();
+ }
+
+ static class Factory {
+
+ private final KeyguardSecurityContainer mView;
+ private final AdminSecondaryLockScreenController.Factory
+ mAdminSecondaryLockScreenControllerFactory;
+ private final LockPatternUtils mLockPatternUtils;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final KeyguardSecurityModel mKeyguardSecurityModel;
+ private final MetricsLogger mMetricsLogger;
+ private final UiEventLogger mUiEventLogger;
+ private final KeyguardStateController mKeyguardStateController;
+ private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
+
+ @Inject
+ Factory(KeyguardSecurityContainer view,
+ AdminSecondaryLockScreenController.Factory
+ adminSecondaryLockScreenControllerFactory,
+ LockPatternUtils lockPatternUtils,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardSecurityModel keyguardSecurityModel,
+ MetricsLogger metricsLogger,
+ UiEventLogger uiEventLogger,
+ KeyguardStateController keyguardStateController,
+ KeyguardSecurityViewFlipperController securityViewFlipperController) {
+ mView = view;
+ mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
+ mLockPatternUtils = lockPatternUtils;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mKeyguardSecurityModel = keyguardSecurityModel;
+ mMetricsLogger = metricsLogger;
+ mUiEventLogger = uiEventLogger;
+ mKeyguardStateController = keyguardStateController;
+ mSecurityViewFlipperController = securityViewFlipperController;
+ }
+
+ public KeyguardSecurityContainerController create(
+ SecurityCallback securityCallback) {
+ return new KeyguardSecurityContainerController(mView,
+ mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
+ mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
+ mKeyguardStateController, securityCallback, mSecurityViewFlipperController);
+ }
+
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index ac2160ecb4ae..c77c86711abf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -18,13 +18,14 @@ package com.android.keyguard;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.app.admin.DevicePolicyManager;
-import android.content.Context;
+import android.content.res.Resources;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Inject;
@@ -33,7 +34,7 @@ public class KeyguardSecurityModel {
/**
* The different types of security available.
- * @see KeyguardSecurityContainer#showSecurityScreen
+ * @see KeyguardSecurityContainerController#showSecurityScreen
*/
public enum SecurityMode {
Invalid, // NULL state
@@ -45,21 +46,15 @@ public class KeyguardSecurityModel {
SimPuk // Unlock by entering a sim puk
}
- private final Context mContext;
private final boolean mIsPukScreenAvailable;
- private LockPatternUtils mLockPatternUtils;
+ private final LockPatternUtils mLockPatternUtils;
@Inject
- KeyguardSecurityModel(Context context) {
- mContext = context;
- mLockPatternUtils = new LockPatternUtils(context);
- mIsPukScreenAvailable = mContext.getResources().getBoolean(
+ KeyguardSecurityModel(@Main Resources resources, LockPatternUtils lockPatternUtils) {
+ mIsPukScreenAvailable = resources.getBoolean(
com.android.internal.R.bool.config_enable_puk_unlock_screen);
- }
-
- void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
+ mLockPatternUtils = lockPatternUtils;
}
public SecurityMode getSecurityMode(int userId) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index 43cef3acf147..ac00e9453c97 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -18,11 +18,9 @@ package com.android.keyguard;
import android.content.res.ColorStateList;
import android.view.MotionEvent;
-import com.android.internal.widget.LockPatternUtils;
-
public interface KeyguardSecurityView {
- static public final int SCREEN_ON = 1;
- static public final int VIEW_REVEALED = 2;
+ int SCREEN_ON = 1;
+ int VIEW_REVEALED = 2;
int PROMPT_REASON_NONE = 0;
@@ -63,18 +61,6 @@ public interface KeyguardSecurityView {
int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7;
/**
- * Interface back to keyguard to tell it when security
- * @param callback
- */
- void setKeyguardCallback(KeyguardSecurityCallback callback);
-
- /**
- * Set {@link LockPatternUtils} object. Useful for providing a mock interface.
- * @param utils
- */
- void setLockPatternUtils(LockPatternUtils utils);
-
- /**
* Reset the view and prepare to take input. This should do things like clearing the
* password or pattern and clear error messages.
*/
@@ -101,12 +87,6 @@ public interface KeyguardSecurityView {
boolean needsInput();
/**
- * Get {@link KeyguardSecurityCallback} for the given object
- * @return KeyguardSecurityCallback
- */
- KeyguardSecurityCallback getCallback();
-
- /**
* Show a string explaining why the security view needs to be solved.
*
* @param reason a flag indicating which string should be shown, see {@link #PROMPT_REASON_NONE}
@@ -123,12 +103,6 @@ public interface KeyguardSecurityView {
void showMessage(CharSequence message, ColorStateList colorState);
/**
- * Instruct the view to show usability hints, if any.
- *
- */
- void showUsabilityHint();
-
- /**
* Starts the animation which should run when the security view appears.
*/
void startAppearAnimation();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java
deleted file mode 100644
index ef9ba19fbb43..000000000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import android.view.View;
-
-import com.android.systemui.util.ViewController;
-
-import javax.inject.Inject;
-
-
-/** Controller for a {@link KeyguardSecurityView}. */
-public class KeyguardSecurityViewController extends ViewController<View> {
-
- private final KeyguardSecurityView mView;
-
- private KeyguardSecurityViewController(KeyguardSecurityView view) {
- super((View) view);
- // KeyguardSecurityView isn't actually a View, so we need to track it ourselves.
- mView = view;
- }
-
- @Override
- protected void onViewAttached() {
-
- }
-
- @Override
- protected void onViewDetached() {
-
- }
-
- /** Factory for a {@link KeyguardSecurityViewController}. */
- public static class Factory {
- @Inject
- public Factory() {
- }
-
- /** Create a new {@link KeyguardSecurityViewController}. */
- public KeyguardSecurityViewController create(KeyguardSecurityView view) {
- return new KeyguardSecurityViewController(view);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 24da3ad46f23..b8439af6daaa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -18,7 +18,6 @@ package com.android.keyguard;
import android.annotation.NonNull;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -31,7 +30,6 @@ import android.view.ViewHierarchyEncoder;
import android.widget.FrameLayout;
import android.widget.ViewFlipper;
-import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
/**
@@ -39,7 +37,7 @@ import com.android.systemui.R;
* we can emulate {@link android.view.WindowManager.LayoutParams#FLAG_SLIPPERY} within a view
* hierarchy.
*/
-public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView {
+public class KeyguardSecurityViewFlipper extends ViewFlipper {
private static final String TAG = "KeyguardSecurityViewFlipper";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -69,111 +67,16 @@ public class KeyguardSecurityViewFlipper extends ViewFlipper implements Keyguard
return result;
}
- KeyguardSecurityView getSecurityView() {
+ KeyguardInputView getSecurityView() {
View child = getChildAt(getDisplayedChild());
- if (child instanceof KeyguardSecurityView) {
- return (KeyguardSecurityView) child;
+ if (child instanceof KeyguardInputView) {
+ return (KeyguardInputView) child;
}
return null;
}
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.setKeyguardCallback(callback);
- }
- }
-
- @Override
- public void setLockPatternUtils(LockPatternUtils utils) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.setLockPatternUtils(utils);
- }
- }
-
- @Override
- public void reset() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.reset();
- }
- }
-
- @Override
- public void onPause() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.onPause();
- }
- }
-
- @Override
- public void onResume(int reason) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.onResume(reason);
- }
- }
-
- @Override
- public boolean needsInput() {
- KeyguardSecurityView ksv = getSecurityView();
- return (ksv != null) ? ksv.needsInput() : false;
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- KeyguardSecurityView ksv = getSecurityView();
- return (ksv != null) ? ksv.getCallback() : null;
- }
-
- @Override
- public void showPromptReason(int reason) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.showPromptReason(reason);
- }
- }
-
- @Override
- public void showMessage(CharSequence message, ColorStateList colorState) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.showMessage(message, colorState);
- }
- }
-
- @Override
- public void showUsabilityHint() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.showUsabilityHint();
- }
- }
-
- @Override
- public void startAppearAnimation() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.startAppearAnimation();
- }
- }
-
- @Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- return ksv.startDisappearAnimation(finishRunnable);
- } else {
- return false;
- }
- }
-
- @Override
public CharSequence getTitle() {
- KeyguardSecurityView ksv = getSecurityView();
+ KeyguardInputView ksv = getSecurityView();
if (ksv != null) {
return ksv.getTitle();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
new file mode 100644
index 000000000000..49530355a6fb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -0,0 +1,148 @@
+/*
+ * 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.keyguard;
+
+import android.util.Log;
+import android.view.LayoutInflater;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardInputViewController.Factory;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.systemui.R;
+import com.android.systemui.util.ViewController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for a {@link KeyguardSecurityViewFlipper}.
+ */
+@KeyguardBouncerScope
+public class KeyguardSecurityViewFlipperController
+ extends ViewController<KeyguardSecurityViewFlipper> {
+
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ private static final String TAG = "KeyguardSecurityView";
+
+ private final List<KeyguardInputViewController<KeyguardInputView>> mChildren =
+ new ArrayList<>();
+ private final LayoutInflater mLayoutInflater;
+ private final Factory mKeyguardSecurityViewControllerFactory;
+
+ @Inject
+ protected KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view,
+ LayoutInflater layoutInflater,
+ KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory) {
+ super(view);
+ mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
+ mLayoutInflater = layoutInflater;
+ }
+
+ @Override
+ protected void onViewAttached() {
+
+ }
+
+ @Override
+ protected void onViewDetached() {
+
+ }
+
+ public void reset() {
+ for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
+ child.reset();
+ }
+ }
+
+ @VisibleForTesting
+ KeyguardInputViewController<KeyguardInputView> getSecurityView(SecurityMode securityMode,
+ KeyguardSecurityCallback keyguardSecurityCallback) {
+ KeyguardInputViewController<KeyguardInputView> childController = null;
+ for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
+ if (child.getSecurityMode() == securityMode) {
+ childController = child;
+ break;
+ }
+ }
+
+ if (childController == null
+ && securityMode != SecurityMode.None && securityMode != SecurityMode.Invalid) {
+
+ int layoutId = getLayoutIdFor(securityMode);
+ KeyguardInputView view = null;
+ if (layoutId != 0) {
+ if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
+ view = (KeyguardInputView) mLayoutInflater.inflate(
+ layoutId, mView, false);
+ mView.addView(view);
+ childController = mKeyguardSecurityViewControllerFactory.create(
+ view, securityMode, keyguardSecurityCallback);
+ childController.init();
+
+ mChildren.add(childController);
+ }
+ }
+
+ if (childController == null) {
+ childController = new NullKeyguardInputViewController(
+ securityMode, keyguardSecurityCallback);
+ }
+
+ return childController;
+ }
+
+ private int getLayoutIdFor(SecurityMode securityMode) {
+ switch (securityMode) {
+ case Pattern: return com.android.systemui.R.layout.keyguard_pattern_view;
+ case PIN: return com.android.systemui.R.layout.keyguard_pin_view;
+ case Password: return com.android.systemui.R.layout.keyguard_password_view;
+ case SimPin: return com.android.systemui.R.layout.keyguard_sim_pin_view;
+ case SimPuk: return R.layout.keyguard_sim_puk_view;
+ default:
+ return 0;
+ }
+ }
+
+ /** Makes the supplied child visible if it is contained win this view, */
+ public void show(KeyguardInputViewController<KeyguardInputView> childController) {
+ int index = childController.getIndexIn(mView);
+ if (index != -1) {
+ mView.setDisplayedChild(index);
+ }
+ }
+
+ private static class NullKeyguardInputViewController
+ extends KeyguardInputViewController<KeyguardInputView> {
+ protected NullKeyguardInputViewController(SecurityMode securityMode,
+ KeyguardSecurityCallback keyguardSecurityCallback) {
+ super(null, securityMode, keyguardSecurityCallback);
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void onStartingToHide() {
+
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 1c47aa0151f0..c0f9ce794628 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -16,66 +16,19 @@
package com.android.keyguard;
-import android.annotation.NonNull;
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
-import android.app.Dialog;
-import android.app.ProgressDialog;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.telephony.PinResult;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
* Displays a PIN pad for unlocking.
*/
public class KeyguardSimPinView extends KeyguardPinBasedInputView {
- private static final String LOG_TAG = "KeyguardSimPinView";
- private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
public static final String TAG = "KeyguardSimPinView";
- private ProgressDialog mSimUnlockProgressDialog = null;
- private CheckSimPin mCheckSimPinThread;
-
- // Below flag is set to true during power-up or when a new SIM card inserted on device.
- // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
- // be displayed to inform user about the number of remaining PIN attempts left.
- private boolean mShowDefaultMessage = true;
- private int mRemainingAttempts = -1;
- private AlertDialog mRemainingAttemptsDialog;
- private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private ImageView mSimImageView;
-
- KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onSimStateChanged(int subId, int slotId, int simState) {
- if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
- switch(simState) {
- case TelephonyManager.SIM_STATE_READY: {
- mRemainingAttempts = -1;
- resetState();
- break;
- }
- default:
- resetState();
- }
- }
- };
-
public KeyguardSimPinView(Context context) {
this(context, null);
}
@@ -84,81 +37,9 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
super(context, attrs);
}
- @Override
- public void resetState() {
- super.resetState();
- if (DEBUG) Log.v(TAG, "Resetting state");
- handleSubInfoChangeIfNeeded();
- if (mShowDefaultMessage) {
- showDefaultMessage();
- }
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
-
+ public void setEsimLocked(boolean locked) {
KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
- esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
- }
-
- private void setLockedSimMessage() {
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
- int count = 1;
- TelephonyManager telephonyManager =
- (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- if (telephonyManager != null) {
- count = telephonyManager.getActiveModemCount();
- }
- Resources rez = getResources();
- String msg;
- TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor });
- int color = array.getColor(0, Color.WHITE);
- array.recycle();
- if (count < 2) {
- msg = rez.getString(R.string.kg_sim_pin_instructions);
- } else {
- SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
- .getSubscriptionInfoForSubId(mSubId);
- CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
- msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
- if (info != null) {
- color = info.getIconTint();
- }
- }
- if (isEsimLocked) {
- msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
- }
-
- if (mSecurityMessageDisplay != null && getVisibility() == VISIBLE) {
- mSecurityMessageDisplay.setMessage(msg);
- }
- mSimImageView.setImageTintList(ColorStateList.valueOf(color));
- }
-
- private void showDefaultMessage() {
- setLockedSimMessage();
- if (mRemainingAttempts >= 0) {
- return;
- }
-
- // Sending empty PIN here to query the number of remaining PIN attempts
- new CheckSimPin("", mSubId) {
- void onSimCheckResponse(final PinResult result) {
- Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
- + result.toString());
- if (result.getAttemptsRemaining() >= 0) {
- mRemainingAttempts = result.getAttemptsRemaining();
- setLockedSimMessage();
- }
- }
- }.start();
- }
-
- private void handleSubInfoChangeIfNeeded() {
- KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class);
- int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED);
- if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
- mSubId = subId;
- mShowDefaultMessage = true;
- mRemainingAttempts = -1;
- }
+ esimButton.setVisibility(locked ? View.VISIBLE : View.GONE);
}
@Override
@@ -173,35 +54,6 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
return 0;
}
- private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
- String displayMessage;
- int msgId;
- if (attemptsRemaining == 0) {
- displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked);
- } else if (attemptsRemaining > 0) {
- msgId = isDefault ? R.plurals.kg_password_default_pin_message :
- R.plurals.kg_password_wrong_pin_code;
- displayMessage = getContext().getResources()
- .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
- } else {
- msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
- displayMessage = getContext().getString(msgId);
- }
- if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
- displayMessage = getResources()
- .getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
- }
- if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:"
- + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
- return displayMessage;
- }
-
- @Override
- protected boolean shouldLockout(long deadline) {
- // SIM PIN doesn't have a timed lockout
- return false;
- }
-
@Override
protected int getPasswordTextViewId() {
return R.id.simPinEntry;
@@ -214,173 +66,6 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
if (mEcaView instanceof EmergencyCarrierArea) {
((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
}
- mSimImageView = findViewById(R.id.keyguard_sim);
- }
-
- @Override
- public void showUsabilityHint() {
-
- }
-
- @Override
- public void onResume(int reason) {
- super.onResume(reason);
- Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback);
- resetState();
- }
-
- @Override
- public void onPause() {
- // dismiss the dialog.
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.dismiss();
- mSimUnlockProgressDialog = null;
- }
- Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback);
- }
-
- /**
- * Since the IPC can block, we want to run the request in a separate thread
- * with a callback.
- */
- private abstract class CheckSimPin extends Thread {
- private final String mPin;
- private int mSubId;
-
- protected CheckSimPin(String pin, int subId) {
- mPin = pin;
- mSubId = subId;
- }
-
- abstract void onSimCheckResponse(@NonNull PinResult result);
-
- @Override
- public void run() {
- if (DEBUG) {
- Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
- }
- TelephonyManager telephonyManager =
- ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
- .createForSubscriptionId(mSubId);
- final PinResult result = telephonyManager.supplyPinReportPinResult(mPin);
- if (result == null) {
- Log.e(TAG, "Error result for supplyPinReportResult.");
- post(new Runnable() {
- @Override
- public void run() {
- onSimCheckResponse(PinResult.getDefaultFailedResult());
- }
- });
- } else {
- if (DEBUG) {
- Log.v(TAG, "supplyPinReportResult returned: " + result.toString());
- }
- post(new Runnable() {
- @Override
- public void run() {
- onSimCheckResponse(result);
- }
- });
- }
- }
- }
-
- private Dialog getSimUnlockProgressDialog() {
- if (mSimUnlockProgressDialog == null) {
- mSimUnlockProgressDialog = new ProgressDialog(mContext);
- mSimUnlockProgressDialog.setMessage(
- mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
- mSimUnlockProgressDialog.setIndeterminate(true);
- mSimUnlockProgressDialog.setCancelable(false);
- mSimUnlockProgressDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
- return mSimUnlockProgressDialog;
- }
-
- private Dialog getSimRemainingAttemptsDialog(int remaining) {
- String msg = getPinPasswordErrorMessage(remaining, false);
- if (mRemainingAttemptsDialog == null) {
- Builder builder = new AlertDialog.Builder(mContext);
- builder.setMessage(msg);
- builder.setCancelable(false);
- builder.setNeutralButton(R.string.ok, null);
- mRemainingAttemptsDialog = builder.create();
- mRemainingAttemptsDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- } else {
- mRemainingAttemptsDialog.setMessage(msg);
- }
- return mRemainingAttemptsDialog;
- }
-
- @Override
- protected void verifyPasswordAndUnlock() {
- String entry = mPasswordEntry.getText();
-
- if (entry.length() < 4) {
- // otherwise, display a message to the user, and don't submit.
- mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint);
- resetPasswordText(true /* animate */, true /* announce */);
- mCallback.userActivity();
- return;
- }
-
- getSimUnlockProgressDialog().show();
-
- if (mCheckSimPinThread == null) {
- mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
- @Override
- void onSimCheckResponse(final PinResult result) {
- post(new Runnable() {
- @Override
- public void run() {
- mRemainingAttempts = result.getAttemptsRemaining();
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.hide();
- }
- resetPasswordText(true /* animate */,
- /* announce */
- result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
- if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
- Dependency.get(KeyguardUpdateMonitor.class)
- .reportSimUnlocked(mSubId);
- mRemainingAttempts = -1;
- mShowDefaultMessage = true;
- if (mCallback != null) {
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
- }
- } else {
- mShowDefaultMessage = false;
- if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
- if (result.getAttemptsRemaining() <= 2) {
- // this is getting critical - show dialog
- getSimRemainingAttemptsDialog(
- result.getAttemptsRemaining()).show();
- } else {
- // show message
- mSecurityMessageDisplay.setMessage(
- getPinPasswordErrorMessage(
- result.getAttemptsRemaining(), false));
- }
- } else {
- // "PIN operation failed!" - no idea what this was and no way to
- // find out. :/
- mSecurityMessageDisplay.setMessage(getContext().getString(
- R.string.kg_password_pin_failed));
- }
- if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
- + " CheckSimPin.onSimCheckResponse: " + result
- + " attemptsRemaining=" + result.getAttemptsRemaining());
- }
- mCallback.userActivity();
- mCheckSimPinThread = null;
- }
- });
- }
- };
- mCheckSimPinThread.start();
- }
}
@Override
@@ -389,11 +74,6 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
}
@Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- return false;
- }
-
- @Override
public CharSequence getTitle() {
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_sim_pin_unlock);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
new file mode 100644
index 000000000000..cc8bf4f2d028
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -0,0 +1,350 @@
+/*
+ * 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.keyguard;
+
+import android.annotation.NonNull;
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.telephony.PinResult;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+public class KeyguardSimPinViewController
+ extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
+ public static final String TAG = "KeyguardSimPinView";
+ private static final String LOG_TAG = "KeyguardSimPinView";
+ private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final TelephonyManager mTelephonyManager;
+
+ private ProgressDialog mSimUnlockProgressDialog;
+ private CheckSimPin mCheckSimPinThread;
+ private int mRemainingAttempts;
+ // Below flag is set to true during power-up or when a new SIM card inserted on device.
+ // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
+ // be displayed to inform user about the number of remaining PIN attempts left.
+ private boolean mShowDefaultMessage;
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private AlertDialog mRemainingAttemptsDialog;
+ private ImageView mSimImageView;
+
+ KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onSimStateChanged(int subId, int slotId, int simState) {
+ if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+ if (simState == TelephonyManager.SIM_STATE_READY) {
+ mRemainingAttempts = -1;
+ resetState();
+ } else {
+ resetState();
+ }
+ }
+ };
+
+ protected KeyguardSimPinViewController(KeyguardSimPinView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode, LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ LiftToActivateListener liftToActivateListener,
+ TelephonyManager telephonyManager) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mTelephonyManager = telephonyManager;
+ mSimImageView = mView.findViewById(R.id.keyguard_sim);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+ }
+
+ @Override
+ void resetState() {
+ super.resetState();
+ if (DEBUG) Log.v(TAG, "Resetting state");
+ handleSubInfoChangeIfNeeded();
+ mMessageAreaController.setMessage("");
+ if (mShowDefaultMessage) {
+ showDefaultMessage();
+ }
+
+ mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
+ }
+
+ @Override
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return false;
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+ mView.resetState();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
+
+ // dismiss the dialog.
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.dismiss();
+ mSimUnlockProgressDialog = null;
+ }
+ }
+
+ @Override
+ protected void verifyPasswordAndUnlock() {
+ String entry = mPasswordEntry.getText();
+
+ if (entry.length() < 4) {
+ // otherwise, display a message to the user, and don't submit.
+ mMessageAreaController.setMessage(
+ com.android.systemui.R.string.kg_invalid_sim_pin_hint);
+ mView.resetPasswordText(true /* animate */, true /* announce */);
+ getKeyguardSecurityCallback().userActivity();
+ return;
+ }
+
+ getSimUnlockProgressDialog().show();
+
+ if (mCheckSimPinThread == null) {
+ mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
+ @Override
+ void onSimCheckResponse(final PinResult result) {
+ mView.post(() -> {
+ mRemainingAttempts = result.getAttemptsRemaining();
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ mView.resetPasswordText(true /* animate */,
+ /* announce */
+ result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
+ mKeyguardUpdateMonitor.reportSimUnlocked(mSubId);
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
+ getKeyguardSecurityCallback().dismiss(
+ true, KeyguardUpdateMonitor.getCurrentUser());
+ } else {
+ mShowDefaultMessage = false;
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
+ if (result.getAttemptsRemaining() <= 2) {
+ // this is getting critical - show dialog
+ getSimRemainingAttemptsDialog(
+ result.getAttemptsRemaining()).show();
+ } else {
+ // show message
+ mMessageAreaController.setMessage(
+ getPinPasswordErrorMessage(
+ result.getAttemptsRemaining(), false));
+ }
+ } else {
+ // "PIN operation failed!" - no idea what this was and no way to
+ // find out. :/
+ mMessageAreaController.setMessage(mView.getResources().getString(
+ R.string.kg_password_pin_failed));
+ }
+ if (DEBUG) {
+ Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ + " CheckSimPin.onSimCheckResponse: " + result
+ + " attemptsRemaining=" + result.getAttemptsRemaining());
+ }
+ }
+ getKeyguardSecurityCallback().userActivity();
+ mCheckSimPinThread = null;
+ });
+ }
+ };
+ mCheckSimPinThread.start();
+ }
+ }
+
+ private Dialog getSimUnlockProgressDialog() {
+ if (mSimUnlockProgressDialog == null) {
+ mSimUnlockProgressDialog = new ProgressDialog(mView.getContext());
+ mSimUnlockProgressDialog.setMessage(
+ mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message));
+ mSimUnlockProgressDialog.setIndeterminate(true);
+ mSimUnlockProgressDialog.setCancelable(false);
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ return mSimUnlockProgressDialog;
+ }
+
+
+ private Dialog getSimRemainingAttemptsDialog(int remaining) {
+ String msg = getPinPasswordErrorMessage(remaining, false);
+ if (mRemainingAttemptsDialog == null) {
+ Builder builder = new AlertDialog.Builder(mView.getContext());
+ builder.setMessage(msg);
+ builder.setCancelable(false);
+ builder.setNeutralButton(R.string.ok, null);
+ mRemainingAttemptsDialog = builder.create();
+ mRemainingAttemptsDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ } else {
+ mRemainingAttemptsDialog.setMessage(msg);
+ }
+ return mRemainingAttemptsDialog;
+ }
+
+
+ private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
+ String displayMessage;
+ int msgId;
+ if (attemptsRemaining == 0) {
+ displayMessage = mView.getResources().getString(
+ R.string.kg_password_wrong_pin_code_pukked);
+ } else if (attemptsRemaining > 0) {
+ msgId = isDefault ? R.plurals.kg_password_default_pin_message :
+ R.plurals.kg_password_wrong_pin_code;
+ displayMessage = mView.getResources()
+ .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
+ } else {
+ msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
+ displayMessage = mView.getResources().getString(msgId);
+ }
+ if (KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)) {
+ displayMessage = mView.getResources()
+ .getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
+ }
+ if (DEBUG) {
+ Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining="
+ + attemptsRemaining + " displayMessage=" + displayMessage);
+ }
+ return displayMessage;
+ }
+
+ private void showDefaultMessage() {
+ setLockedSimMessage();
+ if (mRemainingAttempts >= 0) {
+ return;
+ }
+
+ // Sending empty PIN here to query the number of remaining PIN attempts
+ new CheckSimPin("", mSubId) {
+ void onSimCheckResponse(final PinResult result) {
+ Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
+ + result.toString());
+ if (result.getAttemptsRemaining() >= 0) {
+ mRemainingAttempts = result.getAttemptsRemaining();
+ setLockedSimMessage();
+ }
+ }
+ }.start();
+ }
+
+ /**
+ * Since the IPC can block, we want to run the request in a separate thread
+ * with a callback.
+ */
+ private abstract class CheckSimPin extends Thread {
+ private final String mPin;
+ private int mSubId;
+
+ protected CheckSimPin(String pin, int subId) {
+ mPin = pin;
+ mSubId = subId;
+ }
+
+ abstract void onSimCheckResponse(@NonNull PinResult result);
+
+ @Override
+ public void run() {
+ if (DEBUG) {
+ Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
+ }
+ TelephonyManager telephonyManager =
+ mTelephonyManager.createForSubscriptionId(mSubId);
+ final PinResult result = telephonyManager.supplyPinReportPinResult(mPin);
+ if (result == null) {
+ Log.e(TAG, "Error result for supplyPinReportResult.");
+ mView.post(() -> onSimCheckResponse(PinResult.getDefaultFailedResult()));
+ } else {
+ if (DEBUG) {
+ Log.v(TAG, "supplyPinReportResult returned: " + result.toString());
+ }
+ mView.post(() -> onSimCheckResponse(result));
+ }
+ }
+ }
+
+ private void setLockedSimMessage() {
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
+ int count = 1;
+ if (mTelephonyManager != null) {
+ count = mTelephonyManager.getActiveModemCount();
+ }
+ Resources rez = mView.getResources();
+ String msg;
+ TypedArray array = mView.getContext().obtainStyledAttributes(
+ new int[] { R.attr.wallpaperTextColor });
+ int color = array.getColor(0, Color.WHITE);
+ array.recycle();
+ if (count < 2) {
+ msg = rez.getString(R.string.kg_sim_pin_instructions);
+ } else {
+ SubscriptionInfo info = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(mSubId);
+ CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
+ msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
+ if (info != null) {
+ color = info.getIconTint();
+ }
+ }
+ if (isEsimLocked) {
+ msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
+ }
+
+ if (mView.getVisibility() == View.VISIBLE) {
+ mMessageAreaController.setMessage(msg);
+ }
+ mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+ }
+
+ private void handleSubInfoChangeIfNeeded() {
+ int subId = mKeyguardUpdateMonitor
+ .getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED);
+ if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
+ mSubId = subId;
+ mShowDefaultMessage = true;
+ mRemainingAttempts = -1;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 5148dd709026..0d72c93e9041 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -16,27 +16,10 @@
package com.android.keyguard;
-import android.annotation.NonNull;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.telephony.PinResult;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -44,48 +27,9 @@ import com.android.systemui.R;
* Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier.
*/
public class KeyguardSimPukView extends KeyguardPinBasedInputView {
- private static final String LOG_TAG = "KeyguardSimPukView";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
public static final String TAG = "KeyguardSimPukView";
- private ProgressDialog mSimUnlockProgressDialog = null;
- private CheckSimPuk mCheckSimPukThread;
-
- // Below flag is set to true during power-up or when a new SIM card inserted on device.
- // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would
- // be displayed to inform user about the number of remaining PUK attempts left.
- private boolean mShowDefaultMessage = true;
- private int mRemainingAttempts = -1;
- private String mPukText;
- private String mPinText;
- private StateMachine mStateMachine = new StateMachine();
- private AlertDialog mRemainingAttemptsDialog;
- private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private ImageView mSimImageView;
-
- KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onSimStateChanged(int subId, int slotId, int simState) {
- if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
- switch(simState) {
- // If the SIM is unlocked via a key sequence through the emergency dialer, it will
- // move into the READY state and the PUK lock keyguard should be removed.
- case TelephonyManager.SIM_STATE_READY: {
- mRemainingAttempts = -1;
- mShowDefaultMessage = true;
- // mCallback can be null if onSimStateChanged callback is called when keyguard
- // isn't active.
- if (mCallback != null) {
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
- }
- break;
- }
- default:
- resetState();
- }
- }
- };
-
public KeyguardSimPukView(Context context) {
this(context, null);
}
@@ -94,136 +38,14 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
super(context, attrs);
}
- private class StateMachine {
- final int ENTER_PUK = 0;
- final int ENTER_PIN = 1;
- final int CONFIRM_PIN = 2;
- final int DONE = 3;
- private int state = ENTER_PUK;
-
- public void next() {
- int msg = 0;
- if (state == ENTER_PUK) {
- if (checkPuk()) {
- state = ENTER_PIN;
- msg = R.string.kg_puk_enter_pin_hint;
- } else {
- msg = R.string.kg_invalid_sim_puk_hint;
- }
- } else if (state == ENTER_PIN) {
- if (checkPin()) {
- state = CONFIRM_PIN;
- msg = R.string.kg_enter_confirm_pin_hint;
- } else {
- msg = R.string.kg_invalid_sim_pin_hint;
- }
- } else if (state == CONFIRM_PIN) {
- if (confirmPin()) {
- state = DONE;
- msg = R.string.keyguard_sim_unlock_progress_dialog_message;
- updateSim();
- } else {
- state = ENTER_PIN; // try again?
- msg = R.string.kg_invalid_confirm_pin_hint;
- }
- }
- resetPasswordText(true /* animate */, true /* announce */);
- if (msg != 0) {
- mSecurityMessageDisplay.setMessage(msg);
- }
- }
-
-
- void reset() {
- mPinText="";
- mPukText="";
- state = ENTER_PUK;
- handleSubInfoChangeIfNeeded();
- if (mShowDefaultMessage) {
- showDefaultMessage();
- }
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
-
- KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
- esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
- mPasswordEntry.requestFocus();
- }
-
-
- }
-
- private void showDefaultMessage() {
- if (mRemainingAttempts >= 0) {
- mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage(
- mRemainingAttempts, true));
- return;
- }
-
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
- int count = 1;
- TelephonyManager telephonyManager =
- (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- if (telephonyManager != null) {
- count = telephonyManager.getActiveModemCount();
- }
- Resources rez = getResources();
- String msg;
- TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor });
- int color = array.getColor(0, Color.WHITE);
- array.recycle();
- if (count < 2) {
- msg = rez.getString(R.string.kg_puk_enter_puk_hint);
- } else {
- SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
- .getSubscriptionInfoForSubId(mSubId);
- CharSequence displayName = info != null ? info.getDisplayName() : "";
- msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
- if (info != null) {
- color = info.getIconTint();
- }
- }
- if (isEsimLocked) {
- msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
- }
- if (mSecurityMessageDisplay != null) {
- mSecurityMessageDisplay.setMessage(msg);
- }
- mSimImageView.setImageTintList(ColorStateList.valueOf(color));
-
- // Sending empty PUK here to query the number of remaining PIN attempts
- new CheckSimPuk("", "", mSubId) {
- void onSimLockChangedResponse(final PinResult result) {
- if (result == null) Log.e(LOG_TAG, "onSimCheckResponse, pin result is NULL");
- else {
- Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
- + result.toString());
- if (result.getAttemptsRemaining() >= 0) {
- mRemainingAttempts = result.getAttemptsRemaining();
- mSecurityMessageDisplay.setMessage(
- getPukPasswordErrorMessage(result.getAttemptsRemaining(), true));
- }
- }
- }
- }.start();
- }
-
- private void handleSubInfoChangeIfNeeded() {
- KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class);
- int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PUK_REQUIRED);
- if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
- mSubId = subId;
- mShowDefaultMessage = true;
- mRemainingAttempts = -1;
- }
- }
-
@Override
protected int getPromptReasonStringRes(int reason) {
// No message on SIM Puk
return 0;
}
- private String getPukPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
+ String getPukPasswordErrorMessage(
+ int attemptsRemaining, boolean isDefault, boolean isEsimLocked) {
String displayMessage;
if (attemptsRemaining == 0) {
@@ -238,28 +60,19 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
R.string.kg_password_puk_failed;
displayMessage = getContext().getString(msgId);
}
- if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
+ if (isEsimLocked) {
displayMessage = getResources()
.getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
}
- if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:"
- + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
+ if (DEBUG) {
+ Log.d(TAG, "getPukPasswordErrorMessage:"
+ + " attemptsRemaining=" + attemptsRemaining
+ + " displayMessage=" + displayMessage);
+ }
return displayMessage;
}
@Override
- public void resetState() {
- super.resetState();
- mStateMachine.reset();
- }
-
- @Override
- protected boolean shouldLockout(long deadline) {
- // SIM PUK doesn't have a timed lockout
- return false;
- }
-
- @Override
protected int getPasswordTextViewId() {
return R.id.pukEntry;
}
@@ -271,197 +84,6 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
if (mEcaView instanceof EmergencyCarrierArea) {
((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
}
- mSimImageView = findViewById(R.id.keyguard_sim);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback);
- resetState();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback);
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
- @Override
- public void onPause() {
- // dismiss the dialog.
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.dismiss();
- mSimUnlockProgressDialog = null;
- }
- }
-
- /**
- * Since the IPC can block, we want to run the request in a separate thread
- * with a callback.
- */
- private abstract class CheckSimPuk extends Thread {
-
- private final String mPin, mPuk;
- private final int mSubId;
-
- protected CheckSimPuk(String puk, String pin, int subId) {
- mPuk = puk;
- mPin = pin;
- mSubId = subId;
- }
-
- abstract void onSimLockChangedResponse(@NonNull PinResult result);
-
- @Override
- public void run() {
- if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
- TelephonyManager telephonyManager =
- ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
- .createForSubscriptionId(mSubId);
- final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin);
- if (result == null) {
- Log.e(TAG, "Error result for supplyPukReportResult.");
- post(new Runnable() {
- @Override
- public void run() {
- onSimLockChangedResponse(PinResult.getDefaultFailedResult());
- }
- });
- } else {
- if (DEBUG) {
- Log.v(TAG, "supplyPukReportResult returned: " + result.toString());
- }
- post(new Runnable() {
- @Override
- public void run() {
- onSimLockChangedResponse(result);
- }
- });
- }
- }
- }
-
- private Dialog getSimUnlockProgressDialog() {
- if (mSimUnlockProgressDialog == null) {
- mSimUnlockProgressDialog = new ProgressDialog(mContext);
- mSimUnlockProgressDialog.setMessage(
- mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
- mSimUnlockProgressDialog.setIndeterminate(true);
- mSimUnlockProgressDialog.setCancelable(false);
- if (!(mContext instanceof Activity)) {
- mSimUnlockProgressDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
- }
- return mSimUnlockProgressDialog;
- }
-
- private Dialog getPukRemainingAttemptsDialog(int remaining) {
- String msg = getPukPasswordErrorMessage(remaining, false);
- if (mRemainingAttemptsDialog == null) {
- AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
- builder.setMessage(msg);
- builder.setCancelable(false);
- builder.setNeutralButton(R.string.ok, null);
- mRemainingAttemptsDialog = builder.create();
- mRemainingAttemptsDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- } else {
- mRemainingAttemptsDialog.setMessage(msg);
- }
- return mRemainingAttemptsDialog;
- }
-
- private boolean checkPuk() {
- // make sure the puk is at least 8 digits long.
- if (mPasswordEntry.getText().length() == 8) {
- mPukText = mPasswordEntry.getText();
- return true;
- }
- return false;
- }
-
- private boolean checkPin() {
- // make sure the PIN is between 4 and 8 digits
- int length = mPasswordEntry.getText().length();
- if (length >= 4 && length <= 8) {
- mPinText = mPasswordEntry.getText();
- return true;
- }
- return false;
- }
-
- public boolean confirmPin() {
- return mPinText.equals(mPasswordEntry.getText());
- }
-
- private void updateSim() {
- getSimUnlockProgressDialog().show();
-
- if (mCheckSimPukThread == null) {
- mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) {
- @Override
- void onSimLockChangedResponse(final PinResult result) {
- post(new Runnable() {
- @Override
- public void run() {
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.hide();
- }
- resetPasswordText(true /* animate */,
- /* announce */
- result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
- if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
- Dependency.get(KeyguardUpdateMonitor.class)
- .reportSimUnlocked(mSubId);
- mRemainingAttempts = -1;
- mShowDefaultMessage = true;
- if (mCallback != null) {
- mCallback.dismiss(true,
- KeyguardUpdateMonitor.getCurrentUser());
- }
- } else {
- mShowDefaultMessage = false;
- if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
- // show message
- mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage(
- result.getAttemptsRemaining(), false));
- if (result.getAttemptsRemaining() <= 2) {
- // this is getting critical - show dialog
- getPukRemainingAttemptsDialog(
- result.getAttemptsRemaining()).show();
- } else {
- // show message
- mSecurityMessageDisplay.setMessage(
- getPukPasswordErrorMessage(
- result.getAttemptsRemaining(), false));
- }
- } else {
- mSecurityMessageDisplay.setMessage(getContext().getString(
- R.string.kg_password_puk_failed));
- }
- if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
- + " UpdateSim.onSimCheckResponse: "
- + " attemptsRemaining=" + result.getAttemptsRemaining());
- }
- mStateMachine.reset();
- mCheckSimPukThread = null;
- }
- });
- }
- };
- mCheckSimPukThread.start();
- }
- }
-
- @Override
- protected void verifyPasswordAndUnlock() {
- mStateMachine.next();
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
new file mode 100644
index 000000000000..a87374939ba6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -0,0 +1,413 @@
+/*
+ * 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.keyguard;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.telephony.PinResult;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+
+public class KeyguardSimPukViewController
+ extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ public static final String TAG = "KeyguardSimPukView";
+
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final TelephonyManager mTelephonyManager;
+
+ private String mPukText;
+ private String mPinText;
+ private int mRemainingAttempts;
+ // Below flag is set to true during power-up or when a new SIM card inserted on device.
+ // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would
+ // be displayed to inform user about the number of remaining PUK attempts left.
+ private boolean mShowDefaultMessage;
+ private StateMachine mStateMachine = new StateMachine();
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private CheckSimPuk mCheckSimPukThread;
+ private ProgressDialog mSimUnlockProgressDialog;
+
+ KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onSimStateChanged(int subId, int slotId, int simState) {
+ if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+ // If the SIM is unlocked via a key sequence through the emergency dialer, it will
+ // move into the READY state and the PUK lock keyguard should be removed.
+ if (simState == TelephonyManager.SIM_STATE_READY) {
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
+ getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ } else {
+ resetState();
+ }
+ }
+ };
+ private ImageView mSimImageView;
+ private AlertDialog mRemainingAttemptsDialog;
+
+ protected KeyguardSimPukViewController(KeyguardSimPukView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode, LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ LiftToActivateListener liftToActivateListener,
+ TelephonyManager telephonyManager) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mTelephonyManager = telephonyManager;
+ mSimImageView = mView.findViewById(R.id.keyguard_sim);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+ mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
+ }
+
+ @Override
+ void resetState() {
+ super.resetState();
+ mStateMachine.reset();
+ }
+
+ @Override
+ protected void verifyPasswordAndUnlock() {
+ mStateMachine.next();
+ }
+
+ private class StateMachine {
+ static final int ENTER_PUK = 0;
+ static final int ENTER_PIN = 1;
+ static final int CONFIRM_PIN = 2;
+ static final int DONE = 3;
+
+ private int mState = ENTER_PUK;
+
+ public void next() {
+ int msg = 0;
+ if (mState == ENTER_PUK) {
+ if (checkPuk()) {
+ mState = ENTER_PIN;
+ msg = com.android.systemui.R.string.kg_puk_enter_pin_hint;
+ } else {
+ msg = com.android.systemui.R.string.kg_invalid_sim_puk_hint;
+ }
+ } else if (mState == ENTER_PIN) {
+ if (checkPin()) {
+ mState = CONFIRM_PIN;
+ msg = com.android.systemui.R.string.kg_enter_confirm_pin_hint;
+ } else {
+ msg = com.android.systemui.R.string.kg_invalid_sim_pin_hint;
+ }
+ } else if (mState == CONFIRM_PIN) {
+ if (confirmPin()) {
+ mState = DONE;
+ msg = com.android.systemui.R.string.keyguard_sim_unlock_progress_dialog_message;
+ updateSim();
+ } else {
+ mState = ENTER_PIN; // try again?
+ msg = com.android.systemui.R.string.kg_invalid_confirm_pin_hint;
+ }
+ }
+ mView.resetPasswordText(true /* animate */, true /* announce */);
+ if (msg != 0) {
+ mMessageAreaController.setMessage(msg);
+ }
+ }
+
+
+ void reset() {
+ mPinText = "";
+ mPukText = "";
+ mState = ENTER_PUK;
+ handleSubInfoChangeIfNeeded();
+ if (mShowDefaultMessage) {
+ showDefaultMessage();
+ }
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
+
+ KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area);
+ esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
+ mPasswordEntry.requestFocus();
+ }
+ }
+
+ private void showDefaultMessage() {
+ if (mRemainingAttempts >= 0) {
+ mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage(
+ mRemainingAttempts, true,
+ KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
+ return;
+ }
+
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
+ int count = 1;
+ if (mTelephonyManager != null) {
+ count = mTelephonyManager.getActiveModemCount();
+ }
+ Resources rez = mView.getResources();
+ String msg;
+ TypedArray array = mView.getContext().obtainStyledAttributes(
+ new int[] { R.attr.wallpaperTextColor });
+ int color = array.getColor(0, Color.WHITE);
+ array.recycle();
+ if (count < 2) {
+ msg = rez.getString(R.string.kg_puk_enter_puk_hint);
+ } else {
+ SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
+ .getSubscriptionInfoForSubId(mSubId);
+ CharSequence displayName = info != null ? info.getDisplayName() : "";
+ msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
+ if (info != null) {
+ color = info.getIconTint();
+ }
+ }
+ if (isEsimLocked) {
+ msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
+ }
+ mMessageAreaController.setMessage(msg);
+ mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+
+ // Sending empty PUK here to query the number of remaining PIN attempts
+ new CheckSimPuk("", "", mSubId) {
+ void onSimLockChangedResponse(final PinResult result) {
+ if (result == null) Log.e(TAG, "onSimCheckResponse, pin result is NULL");
+ else {
+ Log.d(TAG, "onSimCheckResponse " + " empty One result "
+ + result.toString());
+ if (result.getAttemptsRemaining() >= 0) {
+ mRemainingAttempts = result.getAttemptsRemaining();
+ mMessageAreaController.setMessage(
+ mView.getPukPasswordErrorMessage(
+ result.getAttemptsRemaining(), true,
+ KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
+ }
+ }
+ }
+ }.start();
+ }
+
+ private boolean checkPuk() {
+ // make sure the puk is at least 8 digits long.
+ if (mPasswordEntry.getText().length() == 8) {
+ mPukText = mPasswordEntry.getText();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean checkPin() {
+ // make sure the PIN is between 4 and 8 digits
+ int length = mPasswordEntry.getText().length();
+ if (length >= 4 && length <= 8) {
+ mPinText = mPasswordEntry.getText();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean confirmPin() {
+ return mPinText.equals(mPasswordEntry.getText());
+ }
+
+
+
+
+ private void updateSim() {
+ getSimUnlockProgressDialog().show();
+
+ if (mCheckSimPukThread == null) {
+ mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) {
+ @Override
+ void onSimLockChangedResponse(final PinResult result) {
+ mView.post(() -> {
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ mView.resetPasswordText(true /* animate */,
+ /* announce */
+ result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
+ mKeyguardUpdateMonitor.reportSimUnlocked(mSubId);
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
+
+ getKeyguardSecurityCallback().dismiss(
+ true, KeyguardUpdateMonitor.getCurrentUser());
+ } else {
+ mShowDefaultMessage = false;
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
+ // show message
+ mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage(
+ result.getAttemptsRemaining(), false,
+ KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
+ if (result.getAttemptsRemaining() <= 2) {
+ // this is getting critical - show dialog
+ getPukRemainingAttemptsDialog(
+ result.getAttemptsRemaining()).show();
+ } else {
+ // show message
+ mMessageAreaController.setMessage(
+ mView.getPukPasswordErrorMessage(
+ result.getAttemptsRemaining(), false,
+ KeyguardEsimArea.isEsimLocked(
+ mView.getContext(), mSubId)));
+ }
+ } else {
+ mMessageAreaController.setMessage(mView.getResources().getString(
+ R.string.kg_password_puk_failed));
+ }
+ if (DEBUG) {
+ Log.d(TAG, "verifyPasswordAndUnlock "
+ + " UpdateSim.onSimCheckResponse: "
+ + " attemptsRemaining=" + result.getAttemptsRemaining());
+ }
+ }
+ mStateMachine.reset();
+ mCheckSimPukThread = null;
+ });
+ }
+ };
+ mCheckSimPukThread.start();
+ }
+ }
+
+ @Override
+ protected boolean shouldLockout(long deadline) {
+ // SIM PUK doesn't have a timed lockout
+ return false;
+ }
+
+ private Dialog getSimUnlockProgressDialog() {
+ if (mSimUnlockProgressDialog == null) {
+ mSimUnlockProgressDialog = new ProgressDialog(mView.getContext());
+ mSimUnlockProgressDialog.setMessage(
+ mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message));
+ mSimUnlockProgressDialog.setIndeterminate(true);
+ mSimUnlockProgressDialog.setCancelable(false);
+ if (!(mView.getContext() instanceof Activity)) {
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ }
+ return mSimUnlockProgressDialog;
+ }
+
+ private void handleSubInfoChangeIfNeeded() {
+ int subId = mKeyguardUpdateMonitor.getNextSubIdForState(
+ TelephonyManager.SIM_STATE_PUK_REQUIRED);
+ if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
+ mSubId = subId;
+ mShowDefaultMessage = true;
+ mRemainingAttempts = -1;
+ }
+ }
+
+
+ private Dialog getPukRemainingAttemptsDialog(int remaining) {
+ String msg = mView.getPukPasswordErrorMessage(remaining, false,
+ KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
+ if (mRemainingAttemptsDialog == null) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(mView.getContext());
+ builder.setMessage(msg);
+ builder.setCancelable(false);
+ builder.setNeutralButton(R.string.ok, null);
+ mRemainingAttemptsDialog = builder.create();
+ mRemainingAttemptsDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ } else {
+ mRemainingAttemptsDialog.setMessage(msg);
+ }
+ return mRemainingAttemptsDialog;
+ }
+
+ @Override
+ public void onPause() {
+ // dismiss the dialog.
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.dismiss();
+ mSimUnlockProgressDialog = null;
+ }
+ }
+
+ /**
+ * Since the IPC can block, we want to run the request in a separate thread
+ * with a callback.
+ */
+ private abstract class CheckSimPuk extends Thread {
+
+ private final String mPin, mPuk;
+ private final int mSubId;
+
+ protected CheckSimPuk(String puk, String pin, int subId) {
+ mPuk = puk;
+ mPin = pin;
+ mSubId = subId;
+ }
+
+ abstract void onSimLockChangedResponse(@NonNull PinResult result);
+
+ @Override
+ public void run() {
+ if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
+ TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
+ final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin);
+ if (result == null) {
+ Log.e(TAG, "Error result for supplyPukReportResult.");
+ mView.post(() -> onSimLockChangedResponse(PinResult.getDefaultFailedResult()));
+ } else {
+ if (DEBUG) {
+ Log.v(TAG, "supplyPukReportResult returned: " + result.toString());
+ }
+ mView.post(new Runnable() {
+ @Override
+ public void run() {
+ onSimLockChangedResponse(result);
+ }
+ });
+ }
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
index e59602b1cfff..425e50ed6397 100644
--- a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
+++ b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
@@ -16,11 +16,12 @@
package com.android.keyguard;
-import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
+import javax.inject.Inject;
+
/**
* Hover listener that implements lift-to-activate interaction for
* accessibility. May be added to multiple views.
@@ -31,9 +32,9 @@ class LiftToActivateListener implements View.OnHoverListener {
private boolean mCachedClickableState;
- public LiftToActivateListener(Context context) {
- mAccessibilityManager = (AccessibilityManager) context.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
+ @Inject
+ LiftToActivateListener(AccessibilityManager accessibilityManager) {
+ mAccessibilityManager = accessibilityManager;
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index b0457fce6a1a..2205fdd4267d 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -26,6 +26,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
@@ -90,7 +91,8 @@ public class NumPadKey extends ViewGroup {
}
setOnClickListener(mListener);
- setOnHoverListener(new LiftToActivateListener(context));
+ setOnHoverListener(new LiftToActivateListener(
+ (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE)));
mLockPatternUtils = new LockPatternUtils(context);
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index b6010c8915e7..881108858b51 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -22,6 +22,7 @@ import android.view.ViewGroup;
import com.android.keyguard.KeyguardHostView;
import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardSecurityContainer;
+import com.android.keyguard.KeyguardSecurityViewFlipper;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
@@ -58,7 +59,15 @@ public interface KeyguardBouncerModule {
/** */
@Provides
@KeyguardBouncerScope
- static KeyguardSecurityContainer preovidesKeyguardSecurityContainer(KeyguardHostView hostView) {
+ static KeyguardSecurityContainer providesKeyguardSecurityContainer(KeyguardHostView hostView) {
return hostView.findViewById(R.id.keyguard_security_container);
}
+
+ /** */
+ @Provides
+ @KeyguardBouncerScope
+ static KeyguardSecurityViewFlipper providesKeyguardSecurityViewFlipper(
+ KeyguardSecurityContainer containerView) {
+ return containerView.findViewById(R.id.view_flipper);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 832edf719dbb..f24644bfe312 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -37,7 +37,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -126,6 +126,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.SystemWindows;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -167,6 +168,15 @@ public class Dependency {
* Generic handler on the main thread.
*/
private static final String MAIN_HANDLER_NAME = "main_handler";
+ /**
+ * Generic executor on the main thread.
+ */
+ private static final String MAIN_EXECUTOR_NAME = "main_executor";
+
+ /**
+ * Generic executor on a background thread.
+ */
+ private static final String BACKGROUND_EXECUTOR_NAME = "background_executor";
/**
* An email address to send memory leak reports to by default.
@@ -199,6 +209,17 @@ public class Dependency {
new DependencyKey<>(MAIN_HANDLER_NAME);
/**
+ * Generic executor on the main thread.
+ */
+ public static final DependencyKey<Executor> MAIN_EXECUTOR =
+ new DependencyKey<>(MAIN_EXECUTOR_NAME);
+ /**
+ * Generic executor on a background thread.
+ */
+ public static final DependencyKey<Executor> BACKGROUND_EXECUTOR =
+ new DependencyKey<>(BACKGROUND_EXECUTOR_NAME);
+
+ /**
* An email address to send memory leak reports to by default.
*/
public static final DependencyKey<String> LEAK_REPORT_EMAIL =
@@ -288,7 +309,7 @@ public class Dependency {
@Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
@Inject Lazy<SmartReplyController> mSmartReplyController;
@Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
- @Inject Lazy<BubbleController> mBubbleController;
+ @Inject Lazy<Bubbles> mBubbles;
@Inject Lazy<NotificationEntryManager> mNotificationEntryManager;
@Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
@Inject Lazy<AutoHideController> mAutoHideController;
@@ -301,6 +322,8 @@ public class Dependency {
@Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler;
@Nullable
@Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail;
+ @Inject @Main Lazy<Executor> mMainExecutor;
+ @Inject @Background Lazy<Executor> mBackgroundExecutor;
@Inject Lazy<ClockManager> mClockManager;
@Inject Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
@Inject Lazy<DevicePolicyManagerWrapper> mDevicePolicyManagerWrapper;
@@ -336,6 +359,8 @@ public class Dependency {
mProviders.put(BG_LOOPER, mBgLooper::get);
mProviders.put(MAIN_LOOPER, mMainLooper::get);
mProviders.put(MAIN_HANDLER, mMainHandler::get);
+ mProviders.put(MAIN_EXECUTOR, mMainExecutor::get);
+ mProviders.put(BACKGROUND_EXECUTOR, mBackgroundExecutor::get);
mProviders.put(ActivityStarter.class, mActivityStarter::get);
mProviders.put(BroadcastDispatcher.class, mBroadcastDispatcher::get);
@@ -483,7 +508,7 @@ public class Dependency {
mProviders.put(SmartReplyController.class, mSmartReplyController::get);
mProviders.put(RemoteInputQuickSettingsDisabler.class,
mRemoteInputQuickSettingsDisabler::get);
- mProviders.put(BubbleController.class, mBubbleController::get);
+ mProviders.put(Bubbles.class, mBubbles::get);
mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get);
mProviders.put(ForegroundServiceNotificationListener.class,
mForegroundServiceNotificationListener::get);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index d79c96ea4774..289ffbe518c0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics;
+import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.SuppressLint;
@@ -25,6 +26,7 @@ import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.PowerManager;
import android.os.UserHandle;
@@ -41,6 +43,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
@@ -50,13 +53,22 @@ import com.android.systemui.util.settings.SystemSettings;
import java.io.FileWriter;
import java.io.IOException;
+import java.util.List;
import javax.inject.Inject;
/**
* Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
* and coordinates triggering of the high-brightness mode (HBM).
+ *
+ * Note that the current architecture is designed so that a single {@link UdfpsController}
+ * controls/manages all UDFPS sensors. In other words, a single controller is registered with
+ * {@link com.android.server.biometrics.sensors.fingerprint.FingerprintService}, and interfaces such
+ * as {@link FingerprintManager#onFingerDown(int, int, int, float, float)} or
+ * {@link IUdfpsOverlayController#showUdfpsOverlay(int)}should all have
+ * {@code sensorId} parameters.
*/
+@SuppressWarnings("deprecation")
class UdfpsController implements DozeReceiver {
private static final String TAG = "UdfpsController";
// Gamma approximation for the sRGB color space.
@@ -64,6 +76,10 @@ class UdfpsController implements DozeReceiver {
private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
private final FingerprintManager mFingerprintManager;
+ // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
+ // sensors, this, in addition to a lot of the code here, will be updated.
+ @VisibleForTesting
+ final int mUdfpsSensorId;
private final WindowManager mWindowManager;
private final SystemSettings mSystemSettings;
private final DelayableExecutor mFgExecutor;
@@ -103,17 +119,17 @@ class UdfpsController implements DozeReceiver {
public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
@Override
- public void showUdfpsOverlay() {
+ public void showUdfpsOverlay(int sensorId) {
UdfpsController.this.setShowOverlay(true);
}
@Override
- public void hideUdfpsOverlay() {
+ public void hideUdfpsOverlay(int sensorId) {
UdfpsController.this.setShowOverlay(false);
}
@Override
- public void setDebugMessage(String message) {
+ public void setDebugMessage(int sensorId, String message) {
mView.setDebugMessage(message);
}
}
@@ -165,6 +181,17 @@ class UdfpsController implements DozeReceiver {
mFgExecutor = fgExecutor;
mLayoutParams = createLayoutParams(context);
+ int udfpsSensorId = -1;
+ for (FingerprintSensorProperties props : mFingerprintManager.getSensorProperties()) {
+ if (props.isAnyUdfpsType()) {
+ udfpsSensorId = props.sensorId;
+ break;
+ }
+ }
+ // At least one UDFPS sensor exists
+ checkArgument(udfpsSensorId != -1);
+ mUdfpsSensorId = udfpsSensorId;
+
mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false);
mHbmPath = resources.getString(R.string.udfps_hbm_sysfs_path);
@@ -347,7 +374,7 @@ class UdfpsController implements DozeReceiver {
fw.write(mHbmEnableCommand);
fw.close();
}
- mFingerprintManager.onFingerDown(x, y, minor, major);
+ mFingerprintManager.onFingerDown(mUdfpsSensorId, x, y, minor, major);
} catch (IOException e) {
mView.hideScrimAndDot();
Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage());
@@ -355,7 +382,7 @@ class UdfpsController implements DozeReceiver {
}
private void onFingerUp() {
- mFingerprintManager.onFingerUp();
+ mFingerprintManager.onFingerUp(mUdfpsSensorId);
// Hiding the scrim before disabling HBM results in less noticeable flicker.
mView.hideScrimAndDot();
if (mHbmSupported) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index cbce4d835800..dff405cb162c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -129,7 +129,8 @@ import java.util.Objects;
*
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
-public class BubbleController implements ConfigurationController.ConfigurationListener, Dumpable {
+public class BubbleController implements Bubbles, ConfigurationController.ConfigurationListener,
+ Dumpable {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
@@ -520,6 +521,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
/**
* See {@link NotifCallback}.
*/
+ @Override
public void addNotifCallback(NotifCallback callback) {
mCallbacks.add(callback);
}
@@ -701,6 +703,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
* since we want the scrim's appearance and behavior to be identical to that of the notification
* shade scrim.
*/
+ @Override
public ScrimView getScrimForBubble() {
return mBubbleScrim;
}
@@ -709,6 +712,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
* Called when the status bar has become visible or invisible (either permanently or
* temporarily).
*/
+ @Override
public void onStatusBarVisibilityChanged(boolean visible) {
if (mStackView != null) {
// Hide the stack temporarily if the status bar has been made invisible, and the stack
@@ -726,14 +730,16 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mInflateSynchronously = inflateSynchronously;
}
- void setOverflowListener(BubbleData.Listener listener) {
+ @Override
+ public void setOverflowListener(BubbleData.Listener listener) {
mOverflowListener = listener;
}
/**
* @return Bubbles for updating overflow.
*/
- List<Bubble> getOverflowBubbles() {
+ @Override
+ public List<Bubble> getOverflowBubbles() {
return mBubbleData.getOverflowBubbles();
}
@@ -956,13 +962,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
- boolean inLandscape() {
- return mOrientation == Configuration.ORIENTATION_LANDSCAPE;
- }
-
/**
* Set a listener to be notified of bubble expand events.
*/
+ @Override
public void setExpandListener(BubbleExpandListener listener) {
mExpandListener = ((isExpanding, key) -> {
if (listener != null) {
@@ -988,29 +991,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
return mBubbleData.hasBubbles();
}
- /**
- * Whether the stack of bubbles is expanded or not.
- */
+ @Override
public boolean isStackExpanded() {
return mBubbleData.isExpanded();
}
- /**
- * Tell the stack of bubbles to collapse.
- */
+ @Override
public void collapseStack() {
mBubbleData.setExpanded(false /* expanded */);
}
- /**
- * True if either:
- * (1) There is a bubble associated with the provided key and if its notification is hidden
- * from the shade.
- * (2) There is a group summary associated with the provided key that is hidden from the shade
- * because it has been dismissed but still has child bubbles active.
- *
- * False otherwise.
- */
+ @Override
public boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry) {
String key = entry.getKey();
boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key)
@@ -1022,19 +1013,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
return (isSummary && isSuppressedSummary) || isSuppressedBubble;
}
- /**
- * True if:
- * (1) The current notification entry same as selected bubble notification entry and the
- * stack is currently expanded.
- *
- * False otherwise.
- */
+ @Override
public boolean isBubbleExpanded(NotificationEntry entry) {
return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null
&& mBubbleData.getSelectedBubble().getKey().equals(entry.getKey()) ? true : false;
}
- void promoteBubbleFromOverflow(Bubble bubble) {
+ @Override
+ public void promoteBubbleFromOverflow(Bubble bubble) {
mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
bubble.setInflateSynchronously(mInflateSynchronously);
bubble.setShouldAutoExpand(true);
@@ -1042,12 +1028,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
setIsBubble(bubble, true /* isBubble */);
}
- /**
- * Request the stack expand if needed, then select the specified Bubble as current.
- * If no bubble exists for this entry, one is created.
- *
- * @param entry the notification for the bubble to be selected
- */
+ @Override
public void expandStackAndSelectBubble(NotificationEntry entry) {
if (mStatusBarStateListener.getCurrentState() == SHADE) {
mNotifEntryToExpandOnShadeUnlock = null;
@@ -1075,12 +1056,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
- /**
- * When a notification is marked Priority, expand the stack if needed,
- * then (maybe create and) select the given bubble.
- *
- * @param entry the notification for the bubble to show
- */
+ @Override
public void onUserChangedImportance(NotificationEntry entry) {
try {
int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
@@ -1095,10 +1071,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
- /**
- * Directs a back gesture at the bubble stack. When opened, the current expanded bubble
- * is forwarded a back key down/up pair.
- */
+ @Override
public void performBackPressIfNeeded() {
if (mStackView != null) {
mStackView.performBackPressIfNeeded();
@@ -1162,15 +1135,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mContext, mStackView, mBubbleIconFactory, false /* skipInflation */);
}
- /**
- * Called when a user has indicated that an active notification should be shown as a bubble.
- * <p>
- * This method will collapse the shade, create the bubble without a flyout or dot, and suppress
- * the notification from appearing in the shade.
- *
- * @param entry the notification to change bubble state for.
- * @param shouldBubble whether the notification should show as a bubble or not.
- */
+ @Override
public void onUserChangedBubble(@NonNull final NotificationEntry entry, boolean shouldBubble) {
NotificationChannel channel = entry.getChannel();
final String appPkg = entry.getSbn().getPackageName();
@@ -1209,13 +1174,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
- /**
- * Removes the bubble with the given key.
- * <p>
- * Must be called from the main thread.
- */
@MainThread
- void removeBubble(String key, int reason) {
+ @Override
+ public void removeBubble(String key, int reason) {
if (mBubbleData.hasAnyBubbleWithKey(key)) {
mBubbleData.dismissBubbleWithKey(key, reason);
}
@@ -1457,16 +1418,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
};
- /**
- * We intercept notification entries (including group summaries) dismissed by the user when
- * there is an active bubble associated with it. We do this so that developers can still
- * cancel it (and hence the bubbles associated with it). However, these intercepted
- * notifications should then be hidden from the shade since the user has cancelled them, so we
- * {@link Bubble#setSuppressNotification}. For the case of suppressed summaries, we also add
- * {@link BubbleData#addSummaryToSuppress}.
- *
- * @return true if we want to intercept the dismissal of the entry, else false.
- */
+ @Override
public boolean handleDismissalInterception(NotificationEntry entry) {
if (entry == null) {
return false;
@@ -1589,10 +1541,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mStackView.updateContentDescription();
}
- /**
- * The display id of the expanded view, if the stack is expanded and not occluded by the
- * status bar, otherwise returns {@link Display#INVALID_DISPLAY}.
- */
+ @Override
public int getExpandedDisplayId(Context context) {
if (mStackView == null) {
return INVALID_DISPLAY;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index ec60cbd175d0..83a816b85d78 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -127,7 +127,7 @@ public class BubbleExpandedView extends LinearLayout {
private boolean mIsOverflow;
- private BubbleController mBubbleController = Dependency.get(BubbleController.class);
+ private Bubbles mBubbles = Dependency.get(Bubbles.class);
private WindowManager mWindowManager;
private ActivityManager mActivityManager;
@@ -168,7 +168,7 @@ public class BubbleExpandedView extends LinearLayout {
+ "bubble=" + getBubbleKey());
}
if (mActivityView == null) {
- mBubbleController.removeBubble(getBubbleKey(),
+ mBubbles.removeBubble(getBubbleKey(),
BubbleController.DISMISS_INVALID_INTENT);
return;
}
@@ -194,7 +194,7 @@ public class BubbleExpandedView extends LinearLayout {
// the bubble again so we'll just remove it.
Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
+ ", " + e.getMessage() + "; removing bubble");
- mBubbleController.removeBubble(getBubbleKey(),
+ mBubbles.removeBubble(getBubbleKey(),
BubbleController.DISMISS_INVALID_INTENT);
}
});
@@ -242,7 +242,7 @@ public class BubbleExpandedView extends LinearLayout {
}
if (mBubble != null) {
// Must post because this is called from a binder thread.
- post(() -> mBubbleController.removeBubble(mBubble.getKey(),
+ post(() -> mBubbles.removeBubble(mBubble.getKey(),
BubbleController.DISMISS_TASK_FINISHED));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 160addc405fa..5fdda9759659 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -60,7 +60,7 @@ public class BubbleOverflowActivity extends Activity {
private TextView mEmptyStateTitle;
private TextView mEmptyStateSubtitle;
private ImageView mEmptyStateImage;
- private BubbleController mBubbleController;
+ private Bubbles mBubbles;
private BubbleOverflowAdapter mAdapter;
private RecyclerView mRecyclerView;
private List<Bubble> mOverflowBubbles = new ArrayList<>();
@@ -71,7 +71,8 @@ public class BubbleOverflowActivity extends Activity {
}
@Override
public boolean canScrollVertically() {
- if (mBubbleController.inLandscape()) {
+ if (getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE) {
return super.canScrollVertically();
}
return false;
@@ -93,8 +94,8 @@ public class BubbleOverflowActivity extends Activity {
}
@Inject
- public BubbleOverflowActivity(BubbleController controller) {
- mBubbleController = controller;
+ public BubbleOverflowActivity(Bubbles bubbles) {
+ mBubbles = bubbles;
}
@Override
@@ -131,15 +132,15 @@ public class BubbleOverflowActivity extends Activity {
final int viewHeight = recyclerViewHeight / rows;
mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles,
- mBubbleController::promoteBubbleFromOverflow, viewWidth, viewHeight);
+ mBubbles::promoteBubbleFromOverflow, viewWidth, viewHeight);
mRecyclerView.setAdapter(mAdapter);
mOverflowBubbles.clear();
- mOverflowBubbles.addAll(mBubbleController.getOverflowBubbles());
+ mOverflowBubbles.addAll(mBubbles.getOverflowBubbles());
mAdapter.notifyDataSetChanged();
updateEmptyStateVisibility();
- mBubbleController.setOverflowListener(mDataListener);
+ mBubbles.setOverflowListener(mDataListener);
updateTheme();
}
@@ -209,8 +210,7 @@ public class BubbleOverflowActivity extends Activity {
if (DEBUG_OVERFLOW) {
Log.d(TAG, BubbleDebugConfig.formatBubblesString(
- mBubbleController.getOverflowBubbles(),
- null));
+ mBubbles.getOverflowBubbles(), null));
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
new file mode 100644
index 000000000000..34828b384939
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.view.Display;
+
+import androidx.annotation.MainThread;
+
+import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.phone.ScrimController;
+
+import java.util.List;
+
+/**
+ * Interface to engage bubbles feature.
+ */
+public interface Bubbles {
+
+ /**
+ * @return {@code true} if there is a bubble associated with the provided key and if its
+ * notification is hidden from the shade or there is a group summary associated with the
+ * provided key that is hidden from the shade because it has been dismissed but still has child
+ * bubbles active.
+ */
+ boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry);
+
+ /**
+ * @return {@code true} if the current notification entry same as selected bubble
+ * notification entry and the stack is currently expanded.
+ */
+ boolean isBubbleExpanded(NotificationEntry entry);
+
+ /** @return {@code true} if stack of bubbles is expanded or not. */
+ boolean isStackExpanded();
+
+ /**
+ * @return the {@link ScrimView} drawn behind the bubble stack. This is managed by
+ * {@link ScrimController} since we want the scrim's appearance and behavior to be identical to
+ * that of the notification shade scrim.
+ */
+ ScrimView getScrimForBubble();
+
+ /**
+ * @return the display id of the expanded view, if the stack is expanded and not occluded by the
+ * status bar, otherwise returns {@link Display#INVALID_DISPLAY}.
+ */
+ int getExpandedDisplayId(Context context);
+
+ /** @return Bubbles for updating overflow. */
+ List<Bubble> getOverflowBubbles();
+
+ /** Tell the stack of bubbles to collapse. */
+ void collapseStack();
+
+ /**
+ * Request the stack expand if needed, then select the specified Bubble as current.
+ * If no bubble exists for this entry, one is created.
+ *
+ * @param entry the notification for the bubble to be selected
+ */
+ void expandStackAndSelectBubble(NotificationEntry entry);
+
+
+ /**
+ * Directs a back gesture at the bubble stack. When opened, the current expanded bubble
+ * is forwarded a back key down/up pair.
+ */
+ void performBackPressIfNeeded();
+
+ /** Promote the provided bubbles when overflow view. */
+ void promoteBubbleFromOverflow(Bubble bubble);
+
+ /**
+ * We intercept notification entries (including group summaries) dismissed by the user when
+ * there is an active bubble associated with it. We do this so that developers can still
+ * cancel it (and hence the bubbles associated with it). However, these intercepted
+ * notifications should then be hidden from the shade since the user has cancelled them, so we
+ * {@link Bubble#setSuppressNotification}. For the case of suppressed summaries, we also add
+ * {@link BubbleData#addSummaryToSuppress}.
+ *
+ * @return true if we want to intercept the dismissal of the entry, else false.
+ */
+ boolean handleDismissalInterception(NotificationEntry entry);
+
+ /**
+ * Removes the bubble with the given key.
+ * <p>
+ * Must be called from the main thread.
+ */
+ @MainThread
+ void removeBubble(String key, int reason);
+
+
+ /**
+ * When a notification is marked Priority, expand the stack if needed,
+ * then (maybe create and) select the given bubble.
+ *
+ * @param entry the notification for the bubble to show
+ */
+ void onUserChangedImportance(NotificationEntry entry);
+
+ /**
+ * Called when the status bar has become visible or invisible (either permanently or
+ * temporarily).
+ */
+ void onStatusBarVisibilityChanged(boolean visible);
+
+ /**
+ * Called when a user has indicated that an active notification should be shown as a bubble.
+ * <p>
+ * This method will collapse the shade, create the bubble without a flyout or dot, and suppress
+ * the notification from appearing in the shade.
+ *
+ * @param entry the notification to change bubble state for.
+ * @param shouldBubble whether the notification should show as a bubble or not.
+ */
+ void onUserChangedBubble(@NonNull NotificationEntry entry, boolean shouldBubble);
+
+
+ /** See {@link BubbleController.NotifCallback}. */
+ void addNotifCallback(BubbleController.NotifCallback callback);
+
+ /** Set a listener to be notified of bubble expand events. */
+ void setExpandListener(BubbleController.BubbleExpandListener listener);
+
+ /** Set a listener to be notified of when overflow view update. */
+ void setOverflowListener(BubbleData.Listener listener);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index e835ea206e59..46ef9bc0948e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -74,7 +74,7 @@ public class StackAnimationController extends
*/
public static final int DEFAULT_STIFFNESS = 12000;
public static final float IME_ANIMATION_STIFFNESS = SpringForce.STIFFNESS_LOW;
- private static final int FLING_FOLLOW_STIFFNESS = 20000;
+ private static final int FLING_FOLLOW_STIFFNESS = 500;
public static final float DEFAULT_BOUNCINESS = 0.9f;
private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfig =
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 5bf105368997..08902f8c7803 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -25,6 +25,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubbleData;
import com.android.systemui.bubbles.BubbleDataRepository;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
@@ -53,7 +54,7 @@ public interface BubbleModule {
*/
@SysUISingleton
@Provides
- static BubbleController newBubbleController(
+ static Bubbles newBubbleController(
Context context,
NotificationShadeWindowController notificationShadeWindowController,
StatusBarStateController statusBarStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index c90e6b1360df..e3037543f2e3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -289,6 +289,7 @@ public class DependencyProvider {
/** */
@Provides
+ @SysUISingleton
public LockPatternUtils provideLockPatternUtils(Context context) {
return new LockPatternUtils(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index b35579d3624b..79925bad3cc7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -62,6 +62,7 @@ import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
+import android.view.inputmethod.InputMethodManager;
import com.android.internal.app.IBatteryStats;
import com.android.internal.statusbar.IStatusBarService;
@@ -183,6 +184,12 @@ public class FrameworkServicesModule {
@Provides
@Singleton
+ static InputMethodManager provideInputMethodManager(Context context) {
+ return context.getSystemService(InputMethodManager.class);
+ }
+
+ @Provides
+ @Singleton
static IPackageManager provideIPackageManager() {
return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
}
diff --git a/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java b/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java
new file mode 100644
index 000000000000..dccb24dfe21e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.emergency;
+
+/**
+ * Constants for the Emergency gesture.
+ *
+ * TODO (b/169175022) Update classname and docs when feature name is locked
+ */
+public final class EmergencyGesture {
+
+ /**
+ * Launches the emergency flow.
+ *
+ * <p>The emergency flow is triggered by the Emergency gesture. By default the flow will call
+ * local emergency services, though OEMs can customize the flow.
+ *
+ * <p>This action can only be triggered by System UI through the emergency gesture.
+ *
+ * <p>TODO (b/169175022) Update action name and docs when feature name is locked
+ */
+ public static final String ACTION_LAUNCH_EMERGENCY =
+ "com.android.systemui.action.LAUNCH_EMERGENCY";
+
+ private EmergencyGesture() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index daef2c506ad3..5f726cd1e1f9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -38,6 +38,7 @@ import android.provider.Settings;
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
import android.text.style.StyleSpan;
+import android.util.Log;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -52,6 +53,8 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIAppComponentFactory;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarState;
@@ -62,6 +65,8 @@ import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@@ -80,6 +85,8 @@ public class KeyguardSliceProvider extends SliceProvider implements
NotificationMediaManager.MediaListener, StatusBarStateController.StateListener,
SystemUIAppComponentFactory.ContextInitializer {
+ private static final String TAG = "KgdSliceProvider";
+
private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD);
public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
private static final String KEYGUARD_HEADER_URI =
@@ -310,7 +317,25 @@ public class KeyguardSliceProvider extends SliceProvider implements
mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
mPendingIntent = PendingIntent.getActivity(getContext(), 0,
new Intent(getContext(), KeyguardSliceProvider.class), 0);
- mMediaManager.addCallback(this);
+ try {
+ //TODO(b/168778439): Remove this whole try catch. This is for debugging in dogfood.
+ mMediaManager.addCallback(this);
+ } catch (NullPointerException e) {
+ // We are sometimes failing to set the media manager. Why?
+ Log.w(TAG, "Failed to setup mMediaManager. Trying again.");
+ SysUIComponent rootComponent = SystemUIFactory.getInstance().getSysUIComponent();
+ try {
+ Method injectMethod = rootComponent.getClass()
+ .getMethod("inject", getClass());
+ injectMethod.invoke(rootComponent, this);
+ Log.w("TAG", "mMediaManager is now: " + mMediaManager);
+ } catch (NoSuchMethodException ex) {
+ Log.e(TAG, "Failed to find inject method for KeyguardSliceProvider", ex);
+ } catch (IllegalAccessException | InvocationTargetException ex) {
+ Log.e(TAG, "Failed to call inject", ex);
+ }
+ throw e;
+ }
mStatusBarStateController.addCallback(this);
mNextAlarmController.addCallback(this);
mZenModeController.addCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 810cecca517f..d859fdf7cc82 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -251,10 +251,9 @@ public class MediaControlPanel {
// App icon
ImageView appIcon = mViewHolder.getAppIcon();
if (data.getAppIcon() != null) {
- appIcon.setImageDrawable(data.getAppIcon());
+ appIcon.setImageIcon(data.getAppIcon());
} else {
- Drawable iconDrawable = mContext.getDrawable(R.drawable.ic_music_note);
- appIcon.setImageDrawable(iconDrawable);
+ appIcon.setImageResource(R.drawable.ic_music_note);
}
// Song name
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 40a879abde34..9d92b81ce2ce 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -33,7 +33,7 @@ data class MediaData(
/**
* Icon shown on player, close to app name.
*/
- val appIcon: Drawable?,
+ val appIcon: Icon?,
/**
* Artist name.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index cb6b22c2321f..567384b7a396 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -455,7 +455,7 @@ class MediaDataManager(
val app = builder.loadHeaderAppName()
// App Icon
- val smallIconDrawable: Drawable = sbn.notification.smallIcon.loadDrawable(context)
+ val smallIcon = sbn.notification.smallIcon
// Song name
var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
@@ -518,7 +518,7 @@ class MediaDataManager(
val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
val active = mediaEntries[key]?.active ?: true
onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app,
- smallIconDrawable, artist, song, artWorkIcon, actionIcons,
+ smallIcon, artist, song, artWorkIcon, actionIcons,
actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null,
active, resumeAction = resumeAction, isLocalSession = isLocalSession,
notificationKey = key, hasCheckedForResume = hasCheckedForResume,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index d6b831640326..af851a7b768d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -58,7 +58,7 @@ import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.system.QuickStepContract;
@@ -426,9 +426,9 @@ public class KeyButtonView extends ImageView implements ButtonInterface {
if (getDisplay() != null) {
displayId = getDisplay().getDisplayId();
}
- // Bubble controller will give us a valid display id if it should get the back event
- BubbleController bubbleController = Dependency.get(BubbleController.class);
- int bubbleDisplayId = bubbleController.getExpandedDisplayId(mContext);
+ // Bubbles will give us a valid display id if it should get the back event
+ Bubbles Bubbles = Dependency.get(Bubbles.class);
+ int bubbleDisplayId = Bubbles.getExpandedDisplayId(mContext);
if (mCode == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) {
displayId = bubbleDisplayId;
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 56943604e9b3..6d6d6cbfc780 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -57,7 +57,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -725,9 +725,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
- // Bubble controller will give us a valid display id if it should get the back event
- BubbleController bubbleController = Dependency.get(BubbleController.class);
- int bubbleDisplayId = bubbleController.getExpandedDisplayId(mContext);
+ // Bubbles will give us a valid display id if it should get the back event
+ final int bubbleDisplayId = Dependency.get(Bubbles.class).getExpandedDisplayId(mContext);
if (bubbleDisplayId != INVALID_DISPLAY) {
ev.setDisplayId(bubbleDisplayId);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 9cf2751df8ee..b0fcdf82e2fe 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -325,9 +325,13 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
return;
}
+ final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
+ if (initialConfig == null) {
+ Log.wtf(TAG, "Token not in record, this should not happen mToken=" + mToken);
+ return;
+ }
mPipUiEventLoggerLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
- final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
!= mPipBoundsHandler.getDisplayRotation();
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -548,16 +552,17 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
/**
* Setup the ViewHost and attach the provided menu view to the ViewHost.
+ * @return The input token belonging to the PipMenuView.
*/
- public void attachPipMenuViewHost(View menuView, WindowManager.LayoutParams lp) {
+ public IBinder attachPipMenuViewHost(View menuView, WindowManager.LayoutParams lp) {
if (mPipMenuSurface != null) {
Log.e(TAG, "PIP Menu View already created and attached.");
- return;
+ return null;
}
if (mLeash == null) {
Log.e(TAG, "PiP Leash is not yet ready.");
- return;
+ return null;
}
if (Looper.getMainLooper() != Looper.myLooper()) {
@@ -573,6 +578,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
transaction.setRelativeLayer(mPipMenuSurface, mLeash, 1);
transaction.apply();
mPipViewHost.setView(menuView, lp);
+
+ return mPipViewHost.getSurfacePackage().getInputToken();
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 4c86ea3aac0c..6c232251e817 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -27,10 +27,12 @@ import android.content.pm.ParceledListSlice;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Debug;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.MotionEvent;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.phone.PipMediaController.ActionListener;
@@ -92,6 +94,7 @@ public class PipMenuActivityController {
private int mMenuState;
private PipMenuView mPipMenuView;
+ private IBinder mPipMenuInputToken;
private ActionListener mMediaActionListener = new ActionListener() {
@Override
@@ -120,6 +123,7 @@ public class PipMenuActivityController {
hideMenu();
mPipTaskOrganizer.detachPipMenuViewHost();
mPipMenuView = null;
+ mPipMenuInputToken = null;
}
public void onPinnedStackAnimationEnded() {
@@ -133,7 +137,13 @@ public class PipMenuActivityController {
mPipMenuView = new PipMenuView(mContext, this);
}
- mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, getPipMenuLayoutParams(0, 0));
+
+ // If we haven't gotten the input toekn, that means we haven't had a success attempt
+ // yet at attaching the PipMenuView
+ if (mPipMenuInputToken == null) {
+ mPipMenuInputToken = mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView,
+ getPipMenuLayoutParams(0, 0));
+ }
}
/**
@@ -352,6 +362,13 @@ public class PipMenuActivityController {
// the menu actions to be updated again.
mMediaController.removeListener(mMediaActionListener);
}
+
+ try {
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+ mPipMenuInputToken, menuState != MENU_STATE_NONE /* grantFocus */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to update focus as menu appears/disappears", e);
+ }
}
mMenuState = menuState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
index 1c38ab338969..48ddbffa2d39 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
@@ -154,10 +154,6 @@ public class PipMenuView extends FrameLayout {
expandPip();
}
});
- // TODO (b/161710689): Remove this once focusability for Windowless window is working
- findViewById(R.id.expand_button).setFocusable(false);
- mDismissButton.setFocusable(false);
- mSettingsButton.setFocusable(false);
mResizeHandle = findViewById(R.id.resize_handle);
mResizeHandle.setAlpha(0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index 52a2cecec6b1..0053fea35262 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -2,6 +2,7 @@ package com.android.systemui.qs;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -31,9 +32,6 @@ public class PageIndicator extends ViewGroup {
private static final long ANIMATION_DURATION = 250;
- // The size of a single dot in relation to the whole animation.
- private static final float SINGLE_SCALE = .4f;
-
private static final float MINOR_ALPHA = .42f;
private final ArrayList<Integer> mQueuedPositions = new ArrayList<>();
@@ -75,11 +73,10 @@ public class PageIndicator extends ViewGroup {
}
array.recycle();
- mPageIndicatorWidth =
- (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_width);
- mPageIndicatorHeight =
- (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_height);
- mPageDotWidth = (int) (mPageIndicatorWidth * SINGLE_SCALE);
+ Resources res = context.getResources();
+ mPageIndicatorWidth = res.getDimensionPixelSize(R.dimen.qs_page_indicator_width);
+ mPageIndicatorHeight = res.getDimensionPixelSize(R.dimen.qs_page_indicator_height);
+ mPageDotWidth = res.getDimensionPixelSize(R.dimen.qs_page_indicator_dot_width);
}
public void setNumPages(int numPages) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 22c735d5fa11..04f379ef35ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -177,6 +177,16 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
}
@Override
+ public void endFakeDrag() {
+ try {
+ super.endFakeDrag();
+ } catch (NullPointerException e) {
+ // Not sure what's going on. Let's log it
+ Log.e(TAG, "endFakeDrag called without velocityTracker", e);
+ }
+ }
+
+ @Override
public void computeScroll() {
if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
if (!isFakeDragging()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 2e258d56ece0..8fec5a2acb8d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -30,6 +30,7 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.media.AudioManager;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.provider.AlarmClock;
@@ -64,6 +65,8 @@ import com.android.systemui.BatteryMeterView;
import com.android.systemui.DualToneHandler;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.demomode.DemoMode;
+import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
@@ -151,6 +154,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
private RingerModeTracker mRingerModeTracker;
+ private DemoModeController mDemoModeController;
+ private DemoMode mDemoModeReceiver;
private boolean mAllIndicatorsEnabled;
private boolean mMicCameraIndicatorsEnabled;
@@ -207,7 +212,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
StatusBarIconController statusBarIconController,
ActivityStarter activityStarter, PrivacyItemController privacyItemController,
CommandQueue commandQueue, RingerModeTracker ringerModeTracker,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger, DemoModeController demoModeController) {
super(context, attrs);
mAlarmController = nextAlarmController;
mZenController = zenModeController;
@@ -219,6 +224,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mCommandQueue = commandQueue;
mRingerModeTracker = ringerModeTracker;
mUiEventLogger = uiEventLogger;
+ mDemoModeController = demoModeController;
}
@Override
@@ -268,6 +274,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mClockView = findViewById(R.id.clock);
mClockView.setOnClickListener(this);
+ mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
mDateView = findViewById(R.id.date);
mSpace = findViewById(R.id.space);
@@ -526,6 +533,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
updateStatusText();
});
mStatusBarIconController.addIconGroup(mIconManager);
+ mDemoModeController.addCallback(mDemoModeReceiver);
requestApplyInsets();
}
@@ -606,6 +614,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
setListening(false);
mRingerModeTracker.getRingerModeInternal().removeObservers(this);
mStatusBarIconController.removeIconGroup(mIconManager);
+ mDemoModeController.removeCallback(mDemoModeReceiver);
super.onDetachedFromWindow();
}
@@ -769,4 +778,33 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private boolean getChipEnabled() {
return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled;
}
+
+ private static class ClockDemoModeReceiver implements DemoMode {
+ private Clock mClockView;
+
+ @Override
+ public List<String> demoCommands() {
+ return List.of(COMMAND_CLOCK);
+ }
+
+ ClockDemoModeReceiver(Clock clockView) {
+ mClockView = clockView;
+ }
+
+ @Override
+ public void dispatchDemoCommand(String command, Bundle args) {
+ mClockView.dispatchDemoCommand(command, args);
+ }
+
+ @Override
+ public void onDemoModeStarted() {
+ mClockView.onDemoModeStarted();
+ }
+
+ @Override
+ public void onDemoModeFinished() {
+ mClockView.onDemoModeFinished();
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 47002683c6b9..bbeff6ece902 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -16,30 +16,17 @@
package com.android.systemui.recents;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.app.trust.TrustManager;
import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
-import android.view.Display;
-import android.widget.Toast;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.wm.shell.splitscreen.SplitScreen;
import java.util.Optional;
@@ -56,7 +43,6 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
private final static String TAG = "OverviewProxyRecentsImpl";
@Nullable
private final Lazy<StatusBar> mStatusBarLazy;
- private final Optional<SplitScreen> mSplitScreenOptional;
private Context mContext;
private Handler mHandler;
@@ -65,10 +51,8 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
- public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy,
- Optional<SplitScreen> splitScreenOptional) {
+ public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy) {
mStatusBarLazy = statusBarLazy.orElse(null);
- mSplitScreenOptional = splitScreenOptional;
}
@Override
@@ -140,42 +124,4 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
// Do nothing
}
}
-
- @Override
- public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
- int metricsDockAction) {
- Point realSize = new Point();
- if (initialBounds == null) {
- mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
- .getRealSize(realSize);
- initialBounds = new Rect(0, 0, realSize.x, realSize.y);
- }
-
- ActivityManager.RunningTaskInfo runningTask =
- ActivityManagerWrapper.getInstance().getRunningTask();
- final int activityType = runningTask != null
- ? runningTask.configuration.windowConfiguration.getActivityType()
- : ACTIVITY_TYPE_UNDEFINED;
- boolean screenPinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
- boolean isRunningTaskInHomeOrRecentsStack =
- activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
- if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
- if (runningTask.supportsSplitScreenMultiWindow) {
- if (ActivityManagerWrapper.getInstance().setTaskWindowingModeSplitScreenPrimary(
- runningTask.id, stackCreateMode, initialBounds)) {
- mSplitScreenOptional.ifPresent(splitScreen -> {
- splitScreen.onDockedTopTask();
- // The overview service is handling split screen, so just skip the wait
- // for the first draw and notify the divider to start animating now
- splitScreen.onRecentsDrawn();
- });
- return true;
- }
- } else {
- Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
- Toast.LENGTH_SHORT).show();
- }
- }
- return false;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index df61fd19ad45..6f6dd9cb22c7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -19,7 +19,6 @@ package com.android.systemui.recents;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.provider.Settings;
import com.android.systemui.SystemUI;
@@ -120,17 +119,6 @@ public class Recents extends SystemUI implements CommandQueue.Callbacks {
mImpl.cancelPreloadRecentApps();
}
- public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
- int metricsDockAction) {
- // Ensure the device has been provisioned before allowing the user to interact with
- // recents
- if (!isUserSetup()) {
- return false;
- }
-
- return mImpl.splitPrimaryTask(stackCreateMode, initialBounds, metricsDockAction);
- }
-
/**
* @return whether this device is provisioned and the current user is set up.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
index a641730ac64e..8848dbbda5e7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
@@ -17,7 +17,6 @@ package com.android.systemui.recents;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Rect;
import java.io.PrintWriter;
@@ -35,10 +34,6 @@ public interface RecentsImplementation {
default void showRecentApps(boolean triggeredFromAltTab) {}
default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {}
default void toggleRecentApps() {}
- default boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
- int metricsDockAction) {
- return false;
- }
default void dump(PrintWriter pw) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index b9b4f42a66e1..6202057b9ef1 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -16,9 +16,6 @@
package com.android.systemui.shortcut;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-
import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
@@ -29,7 +26,6 @@ import android.view.WindowManagerGlobal;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.recents.Recents;
import com.android.wm.shell.splitscreen.DividerView;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -46,7 +42,6 @@ public class ShortcutKeyDispatcher extends SystemUI
private static final String TAG = "ShortcutKeyDispatcher";
private final Optional<SplitScreen> mSplitScreenOptional;
- private final Recents mRecents;
private ShortcutKeyServiceProxy mShortcutKeyServiceProxy = new ShortcutKeyServiceProxy(this);
private IWindowManager mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
@@ -60,11 +55,9 @@ public class ShortcutKeyDispatcher extends SystemUI
protected final long SC_DOCK_RIGHT = META_MASK | KeyEvent.KEYCODE_RIGHT_BRACKET;
@Inject
- public ShortcutKeyDispatcher(Context context,
- Optional<SplitScreen> splitScreenOptional, Recents recents) {
+ public ShortcutKeyDispatcher(Context context, Optional<SplitScreen> splitScreenOptional) {
super(context);
mSplitScreenOptional = splitScreenOptional;
- mRecents = recents;
}
/**
@@ -96,8 +89,7 @@ public class ShortcutKeyDispatcher extends SystemUI
}
private void handleDockKey(long shortcutCode) {
- if (mSplitScreenOptional.isPresent()) {
- SplitScreen splitScreen = mSplitScreenOptional.get();
+ mSplitScreenOptional.ifPresent(splitScreen -> {
if (splitScreen.isDividerVisible()) {
// If there is already a docked window, we respond by resizing the docking pane.
DividerView dividerView = splitScreen.getDividerView();
@@ -112,12 +104,9 @@ public class ShortcutKeyDispatcher extends SystemUI
dividerView.stopDragging(target.position, 0f, false /* avoidDismissStart */,
true /* logMetrics */);
return;
+ } else {
+ splitScreen.splitPrimaryTask();
}
- }
-
- // Split the screen
- mRecents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
- ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
- : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 38c7e5c50f63..53179ba4be90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -26,7 +26,7 @@ import android.view.View;
import android.view.ViewGroup;
import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.StatusBarModule;
@@ -47,6 +47,7 @@ import com.android.systemui.util.Assert;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Optional;
import java.util.Stack;
/**
@@ -84,7 +85,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
* possible.
*/
private final boolean mAlwaysExpandNonGroupedNotification;
- private final BubbleController mBubbleController;
+ private final Optional<Bubbles> mBubblesOptional;
private final DynamicPrivacyController mDynamicPrivacyController;
private final KeyguardBypassController mBypassController;
private final ForegroundServiceSectionController mFgsSectionController;
@@ -112,7 +113,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
StatusBarStateController statusBarStateController,
NotificationEntryManager notificationEntryManager,
KeyguardBypassController bypassController,
- BubbleController bubbleController,
+ Optional<Bubbles> bubblesOptional,
DynamicPrivacyController privacyController,
ForegroundServiceSectionController fgsSectionController,
DynamicChildBindController dynamicChildBindController,
@@ -130,7 +131,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
Resources res = context.getResources();
mAlwaysExpandNonGroupedNotification =
res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
- mBubbleController = bubbleController;
+ mBubblesOptional = bubblesOptional;
mDynamicPrivacyController = privacyController;
mDynamicChildBindController = dynamicChildBindController;
mLowPriorityInflationHelper = lowPriorityInflationHelper;
@@ -157,8 +158,10 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
final int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
NotificationEntry ent = activeNotifications.get(i);
+ final boolean isBubbleNotificationSuppressedFromShade = mBubblesOptional.isPresent()
+ && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(ent);
if (ent.isRowDismissed() || ent.isRowRemoved()
- || mBubbleController.isBubbleNotificationSuppressedFromShade(ent)
+ || isBubbleNotificationSuppressedFromShade
|| mFgsSectionController.hasEntry(ent)) {
// we don't want to update removed notifications because they could
// temporarily become children if they were isolated before.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index d15b8476b3c5..969cd90e61d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -21,7 +21,7 @@ import android.content.Context;
import android.os.Handler;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
@@ -59,6 +59,8 @@ import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import java.util.Optional;
+
import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
@@ -162,7 +164,7 @@ public interface StatusBarDependenciesModule {
StatusBarStateController statusBarStateController,
NotificationEntryManager notificationEntryManager,
KeyguardBypassController bypassController,
- BubbleController bubbleController,
+ Optional<Bubbles> bubblesOptional,
DynamicPrivacyController privacyController,
ForegroundServiceSectionController fgsSectionController,
DynamicChildBindController dynamicChildBindController,
@@ -177,7 +179,7 @@ public interface StatusBarDependenciesModule {
statusBarStateController,
notificationEntryManager,
bypassController,
- bubbleController,
+ bubblesOptional,
privacyController,
fgsSectionController,
dynamicChildBindController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index d364689a65d4..7d8979ca1129 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -22,7 +22,7 @@ import android.util.Log;
import android.view.View;
import com.android.systemui.DejankUtils;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -38,19 +38,19 @@ import javax.inject.Inject;
public final class NotificationClicker implements View.OnClickListener {
private static final String TAG = "NotificationClicker";
- private final BubbleController mBubbleController;
private final NotificationClickerLogger mLogger;
- private final Optional<StatusBar> mStatusBar;
+ private final Optional<StatusBar> mStatusBarOptional;
+ private final Optional<Bubbles> mBubblesOptional;
private final NotificationActivityStarter mNotificationActivityStarter;
private NotificationClicker(
- BubbleController bubbleController,
NotificationClickerLogger logger,
- Optional<StatusBar> statusBar,
+ Optional<StatusBar> statusBarOptional,
+ Optional<Bubbles> bubblesOptional,
NotificationActivityStarter notificationActivityStarter) {
- mBubbleController = bubbleController;
mLogger = logger;
- mStatusBar = statusBar;
+ mStatusBarOptional = statusBarOptional;
+ mBubblesOptional = bubblesOptional;
mNotificationActivityStarter = notificationActivityStarter;
}
@@ -61,7 +61,7 @@ public final class NotificationClicker implements View.OnClickListener {
return;
}
- mStatusBar.ifPresent(statusBar -> statusBar.wakeUpIfDozing(
+ mStatusBarOptional.ifPresent(statusBar -> statusBar.wakeUpIfDozing(
SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK"));
final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
@@ -92,8 +92,8 @@ public final class NotificationClicker implements View.OnClickListener {
row.setJustClicked(true);
DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
- if (!row.getEntry().isBubble()) {
- mBubbleController.collapseStack();
+ if (!row.getEntry().isBubble() && mBubblesOptional.isPresent()) {
+ mBubblesOptional.get().collapseStack();
}
mNotificationActivityStarter.onNotificationClicked(entry.getSbn(), row);
@@ -118,26 +118,23 @@ public final class NotificationClicker implements View.OnClickListener {
/** Daggerized builder for NotificationClicker. */
public static class Builder {
- private final BubbleController mBubbleController;
private final NotificationClickerLogger mLogger;
@Inject
- public Builder(
- BubbleController bubbleController,
- NotificationClickerLogger logger) {
- mBubbleController = bubbleController;
+ public Builder(NotificationClickerLogger logger) {
mLogger = logger;
}
/** Builds an instance. */
public NotificationClicker build(
- Optional<StatusBar> statusBar,
+ Optional<StatusBar> statusBarOptional,
+ Optional<Bubbles> bubblesOptional,
NotificationActivityStarter notificationActivityStarter
) {
return new NotificationClicker(
- mBubbleController,
mLogger,
- statusBar,
+ statusBarOptional,
+ bubblesOptional,
notificationActivityStarter);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index 4ddc1dc8498d..0455b0f18afc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -26,6 +27,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Di
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import java.util.HashSet;
+import java.util.Optional;
import java.util.Set;
import javax.inject.Inject;
@@ -54,7 +56,7 @@ import javax.inject.Inject;
public class BubbleCoordinator implements Coordinator {
private static final String TAG = "BubbleCoordinator";
- private final BubbleController mBubbleController;
+ private final Optional<Bubbles> mBubblesOptional;
private final NotifCollection mNotifCollection;
private final Set<String> mInterceptedDismissalEntries = new HashSet<>();
private NotifPipeline mNotifPipeline;
@@ -62,9 +64,9 @@ public class BubbleCoordinator implements Coordinator {
@Inject
public BubbleCoordinator(
- BubbleController bubbleController,
+ Optional<Bubbles> bubblesOptional,
NotifCollection notifCollection) {
- mBubbleController = bubbleController;
+ mBubblesOptional = bubblesOptional;
mNotifCollection = notifCollection;
}
@@ -73,13 +75,17 @@ public class BubbleCoordinator implements Coordinator {
mNotifPipeline = pipeline;
mNotifPipeline.addNotificationDismissInterceptor(mDismissInterceptor);
mNotifPipeline.addFinalizeFilter(mNotifFilter);
- mBubbleController.addNotifCallback(mNotifCallback);
+ if (mBubblesOptional.isPresent()) {
+ mBubblesOptional.get().addNotifCallback(mNotifCallback);
+ }
+
}
private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
@Override
public boolean shouldFilterOut(NotificationEntry entry, long now) {
- return mBubbleController.isBubbleNotificationSuppressedFromShade(entry);
+ return mBubblesOptional.isPresent()
+ && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(entry);
}
};
@@ -97,7 +103,8 @@ public class BubbleCoordinator implements Coordinator {
@Override
public boolean shouldInterceptDismissal(NotificationEntry entry) {
// for experimental bubbles
- if (mBubbleController.handleDismissalInterception(entry)) {
+ if (mBubblesOptional.isPresent()
+ && mBubblesOptional.get().handleDismissalInterception(entry)) {
mInterceptedDismissalEntries.add(entry.getKey());
return true;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index 21d54c85160b..490989dbb39e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -21,9 +21,8 @@ import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
-import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
@@ -43,6 +42,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import javax.inject.Inject;
@@ -64,25 +64,20 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
new ArraySet<>();
private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
+ private final Optional<Lazy<Bubbles>> mBubblesOptional;
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
private boolean mIsUpdatingUnchangedGroup;
- @Nullable private BubbleController mBubbleController = null;
@Inject
public NotificationGroupManagerLegacy(
StatusBarStateController statusBarStateController,
- Lazy<PeopleNotificationIdentifier> peopleNotificationIdentifier) {
+ Lazy<PeopleNotificationIdentifier> peopleNotificationIdentifier,
+ Optional<Lazy<Bubbles>> bubblesOptional) {
statusBarStateController.addCallback(this);
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
- }
-
- private BubbleController getBubbleController() {
- if (mBubbleController == null) {
- mBubbleController = Dependency.get(BubbleController.class);
- }
- return mBubbleController;
+ mBubblesOptional = bubblesOptional;
}
/**
@@ -247,7 +242,8 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
int childCount = 0;
boolean hasBubbles = false;
for (NotificationEntry entry : group.children.values()) {
- if (!getBubbleController().isBubbleNotificationSuppressedFromShade(entry)) {
+ if (mBubblesOptional.isPresent() && !mBubblesOptional.get().get()
+ .isBubbleNotificationSuppressedFromShade(entry)) {
childCount++;
} else {
hasBubbles = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
index 498b8e884b17..1311e3e756dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
@@ -17,11 +17,13 @@
package com.android.systemui.statusbar.notification.collection.render
import android.annotation.StringRes
+import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.android.systemui.R
-import com.android.systemui.statusbar.notification.dagger.HeaderClick
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.notification.dagger.HeaderClickAction
import com.android.systemui.statusbar.notification.dagger.HeaderText
import com.android.systemui.statusbar.notification.dagger.NodeLabel
import com.android.systemui.statusbar.notification.dagger.SectionHeaderScope
@@ -39,11 +41,19 @@ internal class SectionHeaderNodeControllerImpl @Inject constructor(
@NodeLabel override val nodeLabel: String,
private val layoutInflater: LayoutInflater,
@HeaderText @StringRes private val headerTextResId: Int,
- @HeaderClick private val onHeaderClickListener: View.OnClickListener
+ private val activityStarter: ActivityStarter,
+ @HeaderClickAction private val clickIntentAction: String
) : NodeController, SectionHeaderController {
private var _view: SectionHeaderView? = null
private var clearAllClickListener: View.OnClickListener? = null
+ private val onHeaderClickListener = View.OnClickListener {
+ activityStarter.startActivity(
+ Intent(clickIntentAction),
+ true /* onlyProvisioned */,
+ true /* dismissShade */,
+ Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ }
override fun reinflateView(parent: ViewGroup) {
var oldPos = -1
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
index 179d49cb55a1..2a9cfd034dce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
@@ -17,12 +17,9 @@
package com.android.systemui.statusbar.notification.dagger
import android.annotation.StringRes
-import android.content.Intent
import android.provider.Settings
-import android.view.View
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderNodeControllerImpl
@@ -39,18 +36,6 @@ import javax.inject.Scope
object NotificationSectionHeadersModule {
@Provides
- @HeaderClick
- @JvmStatic fun providesOnHeaderClickListener(
- activityStarter: ActivityStarter
- ) = View.OnClickListener {
- activityStarter.startActivity(
- Intent(Settings.ACTION_NOTIFICATION_SETTINGS),
- true /* onlyProvisioned */,
- true /* dismissShade */,
- Intent.FLAG_ACTIVITY_SINGLE_TOP)
- }
-
- @Provides
@IncomingHeader
@SysUISingleton
@JvmStatic fun providesIncomingHeaderSubcomponent(
@@ -58,6 +43,7 @@ object NotificationSectionHeadersModule {
) = builder.get()
.nodeLabel("incoming header")
.headerText(R.string.notification_section_header_incoming)
+ .clickIntentAction(Settings.ACTION_NOTIFICATION_SETTINGS)
.build()
@Provides
@@ -68,6 +54,7 @@ object NotificationSectionHeadersModule {
) = builder.get()
.nodeLabel("alerting header")
.headerText(R.string.notification_section_header_alerting)
+ .clickIntentAction(Settings.ACTION_NOTIFICATION_SETTINGS)
.build()
@Provides
@@ -78,6 +65,7 @@ object NotificationSectionHeadersModule {
) = builder.get()
.nodeLabel("people header")
.headerText(R.string.notification_section_header_conversations)
+ .clickIntentAction(Settings.ACTION_CONVERSATION_SETTINGS)
.build()
@Provides
@@ -88,6 +76,7 @@ object NotificationSectionHeadersModule {
) = builder.get()
.nodeLabel("silent header")
.headerText(R.string.notification_section_header_gentle)
+ .clickIntentAction(Settings.ACTION_NOTIFICATION_SETTINGS)
.build()
@Provides
@@ -151,6 +140,7 @@ interface SectionHeaderControllerSubcomponent {
fun build(): SectionHeaderControllerSubcomponent
@BindsInstance fun nodeLabel(@NodeLabel nodeLabel: String): Builder
@BindsInstance fun headerText(@HeaderText @StringRes headerText: Int): Builder
+ @BindsInstance fun clickIntentAction(@HeaderClickAction clickIntentAction: String): Builder
}
}
@@ -188,7 +178,7 @@ annotation class NodeLabel
@Qualifier
@Retention(AnnotationRetention.BINARY)
-annotation class HeaderClick
+annotation class HeaderClickAction
@Scope
@Retention(AnnotationRetention.BINARY)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 01333f0a47d5..4fff99b482d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -26,7 +26,7 @@ import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -74,6 +74,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
+import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Provider;
@@ -132,7 +133,7 @@ public interface NotificationsModule {
UserContextProvider contextTracker,
Provider<PriorityOnboardingDialogController.Builder> builderProvider,
AssistantFeedbackController assistantFeedbackController,
- BubbleController bubbleController,
+ Optional<Bubbles> bubblesOptional,
UiEventLogger uiEventLogger,
OnUserInteractionCallback onUserInteractionCallback) {
return new NotificationGutsManager(
@@ -149,7 +150,7 @@ public interface NotificationsModule {
contextTracker,
builderProvider,
assistantFeedbackController,
- bubbleController,
+ bubblesOptional,
uiEventLogger,
onUserInteractionCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
index 9da8b8a3fd92..049b471aa7cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.init
import android.service.notification.StatusBarNotification
+import com.android.systemui.bubbles.Bubbles
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.NotificationActivityStarter
@@ -25,6 +26,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.phone.StatusBar
import java.io.FileDescriptor
import java.io.PrintWriter
+import java.util.Optional
/**
* The master controller for all notifications-related work
@@ -35,6 +37,7 @@ import java.io.PrintWriter
interface NotificationsController {
fun initialize(
statusBar: StatusBar,
+ bubblesOptional: Optional<Bubbles>,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
notificationActivityStarter: NotificationActivityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 9fb292878553..45a5d1044b9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.init
import android.service.notification.StatusBarNotification
+import com.android.systemui.bubbles.Bubbles
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
import com.android.systemui.statusbar.FeatureFlags
@@ -75,6 +76,7 @@ class NotificationsControllerImpl @Inject constructor(
override fun initialize(
statusBar: StatusBar,
+ bubblesOptional: Optional<Bubbles>,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
notificationActivityStarter: NotificationActivityStarter,
@@ -90,7 +92,8 @@ class NotificationsControllerImpl @Inject constructor(
listController.bind()
notificationRowBinder.setNotificationClicker(
- clickerBuilder.build(Optional.of(statusBar), notificationActivityStarter))
+ clickerBuilder.build(
+ Optional.of(statusBar), bubblesOptional, notificationActivityStarter))
notificationRowBinder.setUpWithPresenter(
presenter,
listContainer,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
index ded855dd84f9..7569c1bdbb73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.init
import android.service.notification.StatusBarNotification
+import com.android.systemui.bubbles.Bubbles
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
import com.android.systemui.statusbar.NotificationListener
import com.android.systemui.statusbar.NotificationPresenter
@@ -26,6 +27,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.phone.StatusBar
import java.io.FileDescriptor
import java.io.PrintWriter
+import java.util.Optional
import javax.inject.Inject
/**
@@ -37,6 +39,7 @@ class NotificationsControllerStub @Inject constructor(
override fun initialize(
statusBar: StatusBar,
+ bubblesOptional: Optional<Bubbles>,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
notificationActivityStarter: NotificationActivityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 811a72de093c..113c115c44d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -72,7 +72,7 @@ import com.android.internal.widget.CachingIconView;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -1074,7 +1074,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return new View.OnClickListener() {
@Override
public void onClick(View v) {
- Dependency.get(BubbleController.class)
+ Dependency.get(Bubbles.class)
.onUserChangedBubble(mEntry, !mEntry.isBubble() /* createBubble */);
mHeadsUpManager.removeNotification(mEntry.getKey(), true /* releaseImmediately */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index b19997d15664..07a4a188bc48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -67,7 +67,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.NotificationChannelHelper;
@@ -75,6 +75,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.lang.annotation.Retention;
+import java.util.Optional;
import javax.inject.Provider;
@@ -93,7 +94,7 @@ public class NotificationConversationInfo extends LinearLayout implements
private OnUserInteractionCallback mOnUserInteractionCallback;
private Handler mMainHandler;
private Handler mBgHandler;
- private BubbleController mBubbleController;
+ private Optional<Bubbles> mBubblesOptional;
private String mPackageName;
private String mAppName;
private int mAppUid;
@@ -222,7 +223,7 @@ public class NotificationConversationInfo extends LinearLayout implements
@Main Handler mainHandler,
@Background Handler bgHandler,
OnConversationSettingsClickListener onConversationSettingsClickListener,
- BubbleController bubbleController) {
+ Optional<Bubbles> bubblesOptional) {
mSelectedAction = -1;
mINotificationManager = iNotificationManager;
mOnUserInteractionCallback = onUserInteractionCallback;
@@ -241,7 +242,7 @@ public class NotificationConversationInfo extends LinearLayout implements
mIconFactory = conversationIconFactory;
mUserContext = userContext;
mBubbleMetadata = bubbleMetadata;
- mBubbleController = bubbleController;
+ mBubblesOptional = bubblesOptional;
mBuilderProvider = builderProvider;
mMainHandler = mainHandler;
mBgHandler = bgHandler;
@@ -640,9 +641,11 @@ public class NotificationConversationInfo extends LinearLayout implements
mINotificationManager.setBubblesAllowed(mAppPkg, mAppUid,
BUBBLE_PREFERENCE_SELECTED);
}
- post(() -> {
- mBubbleController.onUserChangedImportance(mEntry);
- });
+ if (mBubblesOptional.isPresent()) {
+ post(() -> {
+ mBubblesOptional.get().onUserChangedImportance(mEntry);
+ });
+ }
}
mChannelToUpdate.setImportance(Math.max(
mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 7d418f30e4c5..373f20e6ba96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -47,7 +47,7 @@ import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -70,6 +70,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Optional;
import javax.inject.Provider;
@@ -116,7 +117,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
private final Lazy<StatusBar> mStatusBarLazy;
private final Handler mMainHandler;
private final Handler mBgHandler;
- private final BubbleController mBubbleController;
+ private final Optional<Bubbles> mBubblesOptional;
private Runnable mOpenRunnable;
private final INotificationManager mNotificationManager;
private final LauncherApps mLauncherApps;
@@ -141,7 +142,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
UserContextProvider contextTracker,
Provider<PriorityOnboardingDialogController.Builder> builderProvider,
AssistantFeedbackController assistantFeedbackController,
- BubbleController bubbleController,
+ Optional<Bubbles> bubblesOptional,
UiEventLogger uiEventLogger,
OnUserInteractionCallback onUserInteractionCallback) {
mContext = context;
@@ -157,7 +158,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mBuilderProvider = builderProvider;
mChannelEditorDialogController = channelEditorDialogController;
mAssistantFeedbackController = assistantFeedbackController;
- mBubbleController = bubbleController;
+ mBubblesOptional = bubblesOptional;
mUiEventLogger = uiEventLogger;
mOnUserInteractionCallback = onUserInteractionCallback;
}
@@ -490,7 +491,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mMainHandler,
mBgHandler,
onConversationSettingsListener,
- mBubbleController);
+ mBubblesOptional);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 500de2d29d03..93204995c5b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING;
@@ -73,6 +74,7 @@ import android.widget.ScrollView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardSliceView;
import com.android.settingslib.Utils;
@@ -97,7 +99,6 @@ import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -260,6 +261,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private boolean mDismissAllInProgress;
private boolean mFadeNotificationsOnDismiss;
private FooterDismissListener mFooterDismissListener;
+ private boolean mFlingAfterUpEvent;
/**
* Was the scroller scrolled to the top when the down motion was observed?
@@ -3789,6 +3791,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
float currentOverScrollTop = getCurrentOverScrollAmount(true);
if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
+ mFlingAfterUpEvent = true;
+ setFinishScrollingCallback(() -> {
+ mFlingAfterUpEvent = false;
+ InteractionJankMonitor.getInstance()
+ .end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ setFinishScrollingCallback(null);
+ });
fling(-initialVelocity);
} else {
onOverScrollFling(false, initialVelocity);
@@ -3840,6 +3849,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return true;
}
+ boolean isFlingAfterUpEvent() {
+ return mFlingAfterUpEvent;
+ }
+
@ShadeViewRefactor(RefactorComponent.INPUT)
protected boolean isInsideQsContainer(MotionEvent ev) {
return ev.getY() < mQsContainer.getBottom();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 703c214ed3ac..68208198da68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.stack;
import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
@@ -47,6 +48,7 @@ import android.view.WindowInsets;
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -1556,6 +1558,14 @@ public class NotificationStackScrollLayoutController {
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mView.setCheckForLeaveBehind(true);
}
+
+ // When swiping directly on the NSSL, this would only get an onTouchEvent.
+ // We log any touches other than down, which will be captured by onTouchEvent.
+ // In the intercept we only start tracing when it's not a down (otherwise that down
+ // would be duplicated when intercepted).
+ if (scrollWantsIt && ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
return swipeWantsIt || scrollWantsIt || expandWantsIt;
}
@@ -1611,7 +1621,32 @@ public class NotificationStackScrollLayoutController {
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mView.setCheckForLeaveBehind(true);
}
+ traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt;
}
+
+ private void traceJankOnTouchEvent(int action, boolean scrollerWantsIt) {
+ // Handle interaction jank monitor cases.
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ if (scrollerWantsIt) {
+ InteractionJankMonitor.getInstance()
+ .begin(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (scrollerWantsIt && !mView.isFlingAfterUpEvent()) {
+ InteractionJankMonitor.getInstance()
+ .end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ if (scrollerWantsIt) {
+ InteractionJankMonitor.getInstance()
+ .cancel(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
+ break;
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 95edfe37fee7..d7a8202d7a4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -29,6 +29,7 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.FooterView;
@@ -687,15 +688,27 @@ public class StackScrollAlgorithm {
AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
float childrenOnTop = 0.0f;
+
+ int topHunIndex = -1;
+ for (int i = 0; i < childCount; i++) {
+ ExpandableView child = algorithmState.visibleChildren.get(i);
+ if (child instanceof ActivatableNotificationView
+ && (child.isAboveShelf() || child.showingPulsing())) {
+ topHunIndex = i;
+ break;
+ }
+ }
+
for (int i = childCount - 1; i >= 0; i--) {
childrenOnTop = updateChildZValue(i, childrenOnTop,
- algorithmState, ambientState);
+ algorithmState, ambientState, i == topHunIndex);
}
}
protected float updateChildZValue(int i, float childrenOnTop,
StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState,
+ boolean shouldElevateHun) {
ExpandableView child = algorithmState.visibleChildren.get(i);
ExpandableViewState childViewState = child.getViewState();
int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
@@ -713,8 +726,7 @@ public class StackScrollAlgorithm {
}
childViewState.zTranslation = baseZ
+ childrenOnTop * zDistanceBetweenElements;
- } else if (child == ambientState.getTrackedHeadsUpRow()
- || (i == 0 && (child.isAboveShelf() || child.showingPulsing()))) {
+ } else if (shouldElevateHun) {
// In case this is a new view that has never been measured before, we don't want to
// elevate if we are currently expanded more then the notification
int shelfHeight = ambientState.getShelf() == null ? 0 :
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index b47c59acb82d..0a366c9bb380 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -85,8 +85,6 @@ import com.android.systemui.statusbar.policy.PreviewInflater;
import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory;
import com.android.systemui.tuner.TunerService;
-import java.util.concurrent.Executor;
-
/**
* Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
* text.
@@ -561,7 +559,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
};
if (!mKeyguardStateController.canDismissLockScreen()) {
- Dependency.get(Executor.class).execute(runnable);
+ Dependency.get(Dependency.BACKGROUND_EXECUTOR).execute(runnable);
} else {
boolean dismissShade = !TextUtils.isEmpty(mRightButtonStr)
&& Dependency.get(TunerService.class).getValue(LOCKSCREEN_RIGHT_UNLOCK, 1) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 3d51854a348c..54fb863b5de7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -168,7 +168,7 @@ public class LockIcon extends KeyguardAffordanceView {
int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(newState);
if (!mDrawableCache.contains(iconRes)) {
- mDrawableCache.put(iconRes, getResources().getDrawable(iconRes));
+ mDrawableCache.put(iconRes, getContext().getDrawable(iconRes));
}
return mDrawableCache.get(iconRes);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index bda35fb0a48e..d1c83555c062 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -19,7 +19,7 @@ import com.android.internal.util.ContrastColorUtil;
import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
@@ -40,6 +40,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.function.Function;
import javax.inject.Inject;
@@ -65,7 +66,7 @@ public class NotificationIconAreaController implements
private final NotificationWakeUpCoordinator mWakeUpCoordinator;
private final KeyguardBypassController mBypassController;
private final DozeParameters mDozeParameters;
- private final BubbleController mBubbleController;
+ private final Optional<Bubbles> mBubblesOptional;
private final StatusBarWindowController mStatusBarWindowController;
private int mIconSize;
@@ -114,7 +115,7 @@ public class NotificationIconAreaController implements
NotificationMediaManager notificationMediaManager,
NotificationListener notificationListener,
DozeParameters dozeParameters,
- BubbleController bubbleController,
+ Optional<Bubbles> bubblesOptional,
DemoModeController demoModeController,
DarkIconDispatcher darkIconDispatcher,
StatusBarWindowController statusBarWindowController) {
@@ -127,7 +128,7 @@ public class NotificationIconAreaController implements
mWakeUpCoordinator = wakeUpCoordinator;
wakeUpCoordinator.addListener(this);
mBypassController = keyguardBypassController;
- mBubbleController = bubbleController;
+ mBubblesOptional = bubblesOptional;
mDemoModeController = demoModeController;
mDemoModeController.addCallback(this);
mStatusBarWindowController = statusBarWindowController;
@@ -298,7 +299,7 @@ public class NotificationIconAreaController implements
|| !entry.isPulseSuppressed())) {
return false;
}
- if (mBubbleController.isBubbleExpanded(entry)) {
+ if (mBubblesOptional.isPresent() && mBubblesOptional.get().isBubbleExpanded(entry)) {
return false;
}
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index cd9cc0775c66..3f636ffe3a2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone;
import static android.view.View.GONE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
@@ -62,6 +64,7 @@ import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
@@ -1139,6 +1142,7 @@ public class NotificationPanelViewController extends PanelViewController {
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
mNotificationStackScrollLayoutController.cancelLongPress();
}
break;
@@ -1170,6 +1174,7 @@ public class NotificationPanelViewController extends PanelViewController {
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
mView.getParent().requestDisallowInterceptTouchEvent(true);
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
onQsExpansionStarted();
notifyExpandingFinished();
mInitialHeightOnTouch = mQsExpansionHeight;
@@ -1202,6 +1207,19 @@ public class NotificationPanelViewController extends PanelViewController {
&& x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth();
}
+ private void traceQsJank(boolean startTracing, boolean wasCancelled) {
+ InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
+ if (startTracing) {
+ monitor.begin(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ } else {
+ if (wasCancelled) {
+ monitor.cancel(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ } else {
+ monitor.end(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ }
+ }
+ }
+
private void initDownStates(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mOnlyAffordanceInThisMotion = false;
@@ -1315,9 +1333,9 @@ public class NotificationPanelViewController extends PanelViewController {
final int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
&& mBarState != KEYGUARD && !mQsExpanded && mQsExpansionEnabled) {
-
// Down in the empty area while fully expanded - go to QS.
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
mConflictingQsExpansionGesture = true;
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
@@ -1405,6 +1423,7 @@ public class NotificationPanelViewController extends PanelViewController {
return;
}
mExpectingSynthesizedDown = true;
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
onTrackingStarted();
updatePanelExpanded();
}
@@ -1474,6 +1493,7 @@ public class NotificationPanelViewController extends PanelViewController {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
mInitialTouchY = y;
mInitialTouchX = x;
onQsExpansionStarted();
@@ -1513,6 +1533,9 @@ public class NotificationPanelViewController extends PanelViewController {
if (fraction != 0f || y >= mInitialTouchY) {
flingQsWithCurrentVelocity(y,
event.getActionMasked() == MotionEvent.ACTION_CANCEL);
+ } else {
+ traceQsJank(false /* startTracing */,
+ event.getActionMasked() == MotionEvent.ACTION_CANCEL);
}
if (mQsVelocityTracker != null) {
mQsVelocityTracker.recycle();
@@ -1893,7 +1916,7 @@ public class NotificationPanelViewController extends PanelViewController {
* @see #flingSettings(float, int, Runnable, boolean)
*/
public void flingSettings(float vel, int type) {
- flingSettings(vel, type, null, false /* isClick */);
+ flingSettings(vel, type, null /* onFinishRunnable */, false /* isClick */);
}
/**
@@ -1923,6 +1946,7 @@ public class NotificationPanelViewController extends PanelViewController {
if (onFinishRunnable != null) {
onFinishRunnable.run();
}
+ traceQsJank(false /* startTracing */, type != FLING_EXPAND /* wasCancelled */);
return;
}
@@ -1947,12 +1971,18 @@ public class NotificationPanelViewController extends PanelViewController {
setQsExpansion((Float) animation.getAnimatedValue());
});
animator.addListener(new AnimatorListenerAdapter() {
+ private boolean mIsCanceled;
@Override
public void onAnimationStart(Animator animation) {
notifyExpandingStarted();
}
@Override
+ public void onAnimationCancel(Animator animation) {
+ mIsCanceled = true;
+ }
+
+ @Override
public void onAnimationEnd(Animator animation) {
mAnimatingQS = false;
notifyExpandingFinished();
@@ -1961,6 +1991,7 @@ public class NotificationPanelViewController extends PanelViewController {
if (onFinishRunnable != null) {
onFinishRunnable.run();
}
+ traceQsJank(false /* startTracing */, mIsCanceled /* wasCancelled */);
}
});
// Let's note that we're animating QS. Moving the animator here will cancel it immediately,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index bc80a1a5137d..a4fc3a39c706 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -49,6 +49,7 @@ import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.widget.FrameLayout;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.FloatingToolbar;
import com.android.systemui.R;
@@ -145,6 +146,7 @@ public class NotificationShadeWindowView extends FrameLayout {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setWillNotDraw(!DEBUG);
+ InteractionJankMonitor.getInstance().init(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 6fa99ba41006..5a01f471d0cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.UNLOCK;
@@ -40,6 +41,7 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
import com.android.systemui.DejankUtils;
@@ -109,6 +111,7 @@ public abstract class PanelViewController {
private boolean mMotionAborted;
private boolean mUpwardsWhenThresholdReached;
private boolean mAnimatingOnDown;
+ private boolean mHandlingPointerUp;
private ValueAnimator mHeightAnimator;
private ObjectAnimator mPeekAnimator;
@@ -356,6 +359,9 @@ public abstract class PanelViewController {
protected void startExpandMotion(float newX, float newY, boolean startTracking,
float expandedHeight) {
+ if (!mHandlingPointerUp) {
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ }
mInitialOffsetOnTouch = expandedHeight;
mInitialTouchY = newY;
mInitialTouchX = newX;
@@ -571,6 +577,7 @@ public abstract class PanelViewController {
target = getMaxPanelHeight() - getClearAllHeightWithPadding();
}
if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
+ InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
notifyExpandingFinished();
return;
}
@@ -622,7 +629,12 @@ public abstract class PanelViewController {
}
setAnimator(null);
if (!mCancelled) {
+ InteractionJankMonitor.getInstance()
+ .end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
notifyExpandingFinished();
+ } else {
+ InteractionJankMonitor.getInstance()
+ .cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
}
notifyBarPanelExpansionChanged();
}
@@ -1272,7 +1284,9 @@ public abstract class PanelViewController {
final float newY = event.getY(newIndex);
final float newX = event.getX(newIndex);
mTrackingPointer = event.getPointerId(newIndex);
+ mHandlingPointerUp = true;
startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
+ mHandlingPointerUp = false;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
@@ -1330,6 +1344,12 @@ public abstract class PanelViewController {
case MotionEvent.ACTION_CANCEL:
addMovement(event);
endMotionEvent(event, x, y, false /* forceCancel */);
+ InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ } else {
+ monitor.cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ }
break;
}
return !mGestureWaitForTouchSlop || mTracking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index e95cf2806691..4af27877c201 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -34,6 +34,8 @@ import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
@@ -139,6 +141,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
private ScrimView mScrimInFront;
private ScrimView mScrimBehind;
+ @Nullable
private ScrimView mScrimForBubble;
private Runnable mScrimBehindChangeRunnable;
@@ -238,7 +241,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
* Attach the controller to the supplied views.
*/
public void attachViews(
- ScrimView scrimBehind, ScrimView scrimInFront, ScrimView scrimForBubble) {
+ ScrimView scrimBehind, ScrimView scrimInFront, @Nullable ScrimView scrimForBubble) {
mScrimBehind = scrimBehind;
mScrimInFront = scrimInFront;
mScrimForBubble = scrimForBubble;
@@ -258,7 +261,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
mScrimBehind.setDefaultFocusHighlightEnabled(false);
mScrimInFront.setDefaultFocusHighlightEnabled(false);
- mScrimForBubble.setDefaultFocusHighlightEnabled(false);
+ if (mScrimForBubble != null) {
+ mScrimForBubble.setDefaultFocusHighlightEnabled(false);
+ }
updateScrims();
mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
}
@@ -455,7 +460,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
}
}
- private void setOrAdaptCurrentAnimation(View scrim) {
+ private void setOrAdaptCurrentAnimation(@Nullable View scrim) {
+ if (scrim == null) {
+ return;
+ }
+
float alpha = getCurrentScrimAlpha(scrim);
if (isAnimating(scrim)) {
// Adapt current animation.
@@ -606,11 +615,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
// Only animate scrim color if the scrim view is actually visible
boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0 && !mBlankScreen;
boolean animateScrimBehind = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen;
- boolean animateScrimForBubble = mScrimForBubble.getViewAlpha() != 0 && !mBlankScreen;
mScrimInFront.setColors(mColors, animateScrimInFront);
mScrimBehind.setColors(mColors, animateScrimBehind);
- mScrimForBubble.setColors(mColors, animateScrimForBubble);
// Calculate minimum scrim opacity for white or black text.
int textColor = mColors.supportsDarkText() ? Color.BLACK : Color.WHITE;
@@ -632,7 +639,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
}
setScrimAlpha(mScrimInFront, mInFrontAlpha);
setScrimAlpha(mScrimBehind, mBehindAlpha);
- setScrimAlpha(mScrimForBubble, mBubbleAlpha);
+
+ if (mScrimForBubble != null) {
+ boolean animateScrimForBubble = mScrimForBubble.getViewAlpha() != 0 && !mBlankScreen;
+ mScrimForBubble.setColors(mColors, animateScrimForBubble);
+ setScrimAlpha(mScrimForBubble, mBubbleAlpha);
+ }
// The animation could have all already finished, let's call onFinished just in case
onFinished();
dispatchScrimsVisible();
@@ -828,12 +840,14 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
mBubbleTint = Color.TRANSPARENT;
updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint);
updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint);
- updateScrimColor(mScrimForBubble, mBubbleAlpha, mBubbleTint);
+ if (mScrimForBubble != null) {
+ updateScrimColor(mScrimForBubble, mBubbleAlpha, mBubbleTint);
+ }
}
}
- private boolean isAnimating(View scrim) {
- return scrim.getTag(TAG_KEY_ANIM) != null;
+ private boolean isAnimating(@Nullable View scrim) {
+ return scrim != null && scrim.getTag(TAG_KEY_ANIM) != null;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 2db36f4a62f8..fc91c16f1a48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.phone;
import android.graphics.Color;
import android.os.Trace;
+import androidx.annotation.Nullable;
+
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -212,7 +214,9 @@ public enum ScrimState {
// Set all scrims black, before they fade transparent.
updateScrimColor(mScrimInFront, 1f /* alpha */, Color.BLACK /* tint */);
updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK /* tint */);
- updateScrimColor(mScrimForBubble, 1f /* alpha */, Color.BLACK /* tint */);
+ if (mScrimForBubble != null) {
+ updateScrimColor(mScrimForBubble, 1f /* alpha */, Color.BLACK /* tint */);
+ }
// Scrims should still be black at the end of the transition.
mFrontTint = Color.BLACK;
@@ -258,7 +262,7 @@ public enum ScrimState {
float mDefaultScrimAlpha;
ScrimView mScrimInFront;
ScrimView mScrimBehind;
- ScrimView mScrimForBubble;
+ @Nullable ScrimView mScrimForBubble;
DozeParameters mDozeParameters;
DockManager mDockManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index 1ce22194878f..af2f3e55c9ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -22,7 +22,7 @@ import android.view.ViewTreeObserver;
import android.view.WindowManager;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -31,6 +31,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import java.util.ArrayList;
+import java.util.Optional;
import javax.inject.Inject;
@@ -50,7 +51,7 @@ public class ShadeControllerImpl implements ShadeController {
private final int mDisplayId;
protected final Lazy<StatusBar> mStatusBarLazy;
private final Lazy<AssistManager> mAssistManagerLazy;
- private final Lazy<BubbleController> mBubbleControllerLazy;
+ private final Optional<Lazy<Bubbles>> mBubblesOptional;
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
@@ -63,7 +64,7 @@ public class ShadeControllerImpl implements ShadeController {
WindowManager windowManager,
Lazy<StatusBar> statusBarLazy,
Lazy<AssistManager> assistManagerLazy,
- Lazy<BubbleController> bubbleControllerLazy
+ Optional<Lazy<Bubbles>> bubblesOptional
) {
mCommandQueue = commandQueue;
mStatusBarStateController = statusBarStateController;
@@ -73,7 +74,7 @@ public class ShadeControllerImpl implements ShadeController {
// TODO: Remove circular reference to StatusBar when possible.
mStatusBarLazy = statusBarLazy;
mAssistManagerLazy = assistManagerLazy;
- mBubbleControllerLazy = bubbleControllerLazy;
+ mBubblesOptional = bubblesOptional;
}
@Override
@@ -133,8 +134,8 @@ public class ShadeControllerImpl implements ShadeController {
getStatusBar().getNotificationShadeWindowViewController().cancelExpandHelper();
getStatusBarView().collapsePanel(true /* animate */, delayed, speedUpFactor);
- } else {
- mBubbleControllerLazy.get().collapseStack();
+ } else if (mBubblesOptional.isPresent()) {
+ mBubblesOptional.get().get().collapseStack();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 8254b7f5b32a..e7c29b6b54b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
@@ -37,7 +35,6 @@ import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASL
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_INVALID;
-import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_LEFT;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -147,6 +144,7 @@ import com.android.systemui.SystemUI;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -173,7 +171,6 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.Snoo
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
-import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -649,7 +646,7 @@ public class StatusBar extends SystemUI implements DemoMode,
protected StatusBarNotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
- private final BubbleController mBubbleController;
+ private final Optional<Bubbles> mBubblesOptional;
private final BubbleController.BubbleExpandListener mBubbleExpandListener;
private ActivityIntentHelper mActivityIntentHelper;
@@ -698,7 +695,7 @@ public class StatusBar extends SystemUI implements DemoMode,
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
VibratorHelper vibratorHelper,
- BubbleController bubbleController,
+ Optional<Bubbles> bubblesOptional,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
@@ -717,7 +714,6 @@ public class StatusBar extends SystemUI implements DemoMode,
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
- Optional<Recents> recentsOptional,
Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
Optional<SplitScreen> splitScreenOptional,
@@ -778,7 +774,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mVibratorHelper = vibratorHelper;
- mBubbleController = bubbleController;
+ mBubblesOptional = bubblesOptional;
mVisualStabilityManager = visualStabilityManager;
mDeviceProvisionedController = deviceProvisionedController;
mNavigationBarController = navigationBarController;
@@ -798,7 +794,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy;
mVolumeComponent = volumeComponent;
mCommandQueue = commandQueue;
- mRecentsOptional = recentsOptional;
mStatusBarComponentBuilder = statusBarComponentBuilder;
mPluginManager = pluginManager;
mSplitScreenOptional = splitScreenOptional;
@@ -834,7 +829,10 @@ public class StatusBar extends SystemUI implements DemoMode,
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mBypassHeadsUpNotifier.setUp();
- mBubbleController.setExpandListener(mBubbleExpandListener);
+ if (mBubblesOptional.isPresent()) {
+ mBubblesOptional.get().setExpandListener(mBubbleExpandListener);
+ }
+
mActivityIntentHelper = new ActivityIntentHelper(mContext);
mColorExtractor.addOnColorsChangedListener(this);
@@ -1145,7 +1143,8 @@ public class StatusBar extends SystemUI implements DemoMode,
ScrimView scrimBehind = mNotificationShadeWindowView.findViewById(R.id.scrim_behind);
ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front);
- ScrimView scrimForBubble = mBubbleController.getScrimForBubble();
+ ScrimView scrimForBubble = mBubblesOptional.isPresent()
+ ? mBubblesOptional.get().getScrimForBubble() : null;
mScrimController.setScrimVisibleListener(scrimsVisible -> {
mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible);
@@ -1341,6 +1340,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationsController.initialize(
this,
+ mBubblesOptional,
mPresenter,
mStackScrollerController.getNotificationListContainer(),
mNotificationActivityStarter,
@@ -1542,35 +1542,37 @@ public class StatusBar extends SystemUI implements DemoMode,
}
public boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
- if (!mRecentsOptional.isPresent()) {
+ if (!mSplitScreenOptional.isPresent()) {
return false;
}
- if (mSplitScreenOptional.isPresent()) {
- SplitScreen splitScreen = mSplitScreenOptional.get();
- if (splitScreen.isDividerVisible()) {
- if (splitScreen.isMinimized()
- && !splitScreen.isHomeStackResizable()) {
- // Undocking from the minimized state is not supported
- return false;
- } else {
- splitScreen.onUndockingTask();
- if (metricsUndockAction != -1) {
- mMetricsLogger.action(metricsUndockAction);
- }
- }
- return true;
+ final SplitScreen splitScreen = mSplitScreenOptional.get();
+ if (splitScreen.isDividerVisible()) {
+ if (splitScreen.isMinimized() && !splitScreen.isHomeStackResizable()) {
+ // Undocking from the minimized state is not supported
+ return false;
}
+
+ splitScreen.onUndockingTask();
+ if (metricsUndockAction != -1) {
+ mMetricsLogger.action(metricsUndockAction);
+ }
+ return true;
}
final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(mDisplayId);
if (navbarPos == NAV_BAR_POS_INVALID) {
return false;
}
- int createMode = navbarPos == NAV_BAR_POS_LEFT
- ? SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT
- : SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
- return mRecentsOptional.get().splitPrimaryTask(createMode, null, metricsDockAction);
+
+ if (splitScreen.splitPrimaryTask()) {
+ if (metricsDockAction != -1) {
+ mMetricsLogger.action(metricsDockAction);
+ }
+ return true;
+ }
+
+ return false;
}
/**
@@ -2491,10 +2493,12 @@ public class StatusBar extends SystemUI implements DemoMode,
/** Temporarily hides Bubbles if the status bar is hidden. */
private void updateBubblesVisibility() {
- mBubbleController.onStatusBarVisibilityChanged(
- mStatusBarMode != MODE_LIGHTS_OUT
- && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT
- && !mStatusBarWindowHidden);
+ if (mBubblesOptional.isPresent()) {
+ mBubblesOptional.get().onStatusBarVisibilityChanged(
+ mStatusBarMode != MODE_LIGHTS_OUT
+ && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT
+ && !mStatusBarWindowHidden);
+ }
}
void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
@@ -2817,8 +2821,8 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mRemoteInputManager.getController() != null) {
mRemoteInputManager.getController().closeRemoteInputs();
}
- if (mBubbleController.isStackExpanded()) {
- mBubbleController.collapseStack();
+ if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) {
+ mBubblesOptional.get().collapseStack();
}
if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
int flags = CommandQueue.FLAG_EXCLUDE_NONE;
@@ -2833,9 +2837,9 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mNotificationShadeWindowController != null) {
mNotificationShadeWindowController.setNotTouchable(false);
}
- if (mBubbleController.isStackExpanded()) {
+ if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) {
// Post to main thread handler, since updating the UI.
- mMainThreadHandler.post(() -> mBubbleController.collapseStack());
+ mMainThreadHandler.post(() -> mBubblesOptional.get().collapseStack());
}
finishBarAnimations();
resetUserExpandedStates();
@@ -3535,8 +3539,8 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
if (mNotificationPanelViewController.canPanelBeCollapsed()) {
mShadeController.animateCollapsePanels();
- } else {
- mBubbleController.performBackPressIfNeeded();
+ } else if (mBubblesOptional.isPresent()) {
+ mBubblesOptional.get().performBackPressIfNeeded();
}
return true;
}
@@ -4054,7 +4058,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mScrimController.transitionTo(ScrimState.AOD);
} else if (mIsKeyguard && !unlocking) {
mScrimController.transitionTo(ScrimState.KEYGUARD);
- } else if (mBubbleController.isStackExpanded()) {
+ } else if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) {
mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED, mUnlockScrimCallback);
} else {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
@@ -4113,8 +4117,6 @@ public class StatusBar extends SystemUI implements DemoMode,
protected Display mDisplay;
private int mDisplayId;
- private final Optional<Recents> mRecentsOptional;
-
protected NotificationShelfController mNotificationShelfController;
private final Lazy<AssistManager> mAssistManagerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 737cdeba797a..aa01642e5c17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -48,7 +48,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.EventLogTags;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -77,6 +77,7 @@ import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -103,7 +104,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardManager mKeyguardManager;
private final IDreamManager mDreamManager;
- private final BubbleController mBubbleController;
+ private final Optional<Bubbles> mBubblesOptional;
private final Lazy<AssistManager> mAssistManagerLazy;
private final NotificationRemoteInputManager mRemoteInputManager;
private final GroupMembershipManager mGroupMembershipManager;
@@ -141,7 +142,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
KeyguardManager keyguardManager,
IDreamManager dreamManager,
- BubbleController bubbleController,
+ Optional<Bubbles> bubblesOptional,
Lazy<AssistManager> assistManagerLazy,
NotificationRemoteInputManager remoteInputManager,
GroupMembershipManager groupMembershipManager,
@@ -175,7 +176,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardManager = keyguardManager;
mDreamManager = dreamManager;
- mBubbleController = bubbleController;
+ mBubblesOptional = bubblesOptional;
mAssistManagerLazy = assistManagerLazy;
mRemoteInputManager = remoteInputManager;
mGroupMembershipManager = groupMembershipManager;
@@ -386,11 +387,14 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
}
private void expandBubbleStackOnMainThread(NotificationEntry entry) {
+ if (!mBubblesOptional.isPresent()) {
+ return;
+ }
+
if (Looper.getMainLooper().isCurrentThread()) {
- mBubbleController.expandStackAndSelectBubble(entry);
+ mBubblesOptional.get().expandStackAndSelectBubble(entry);
} else {
- mMainThreadHandler.post(
- () -> mBubbleController.expandStackAndSelectBubble(entry));
+ mMainThreadHandler.post(() -> mBubblesOptional.get().expandStackAndSelectBubble(entry));
}
}
@@ -602,7 +606,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardManager mKeyguardManager;
private final IDreamManager mDreamManager;
- private final BubbleController mBubbleController;
+ private final Optional<Bubbles> mBubblesOptional;
private final Lazy<AssistManager> mAssistManagerLazy;
private final NotificationRemoteInputManager mRemoteInputManager;
private final GroupMembershipManager mGroupMembershipManager;
@@ -639,7 +643,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
KeyguardManager keyguardManager,
IDreamManager dreamManager,
- BubbleController bubbleController,
+ Optional<Bubbles> bubblesOptional,
Lazy<AssistManager> assistManagerLazy,
NotificationRemoteInputManager remoteInputManager,
GroupMembershipManager groupMembershipManager,
@@ -669,7 +673,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardManager = keyguardManager;
mDreamManager = dreamManager;
- mBubbleController = bubbleController;
+ mBubblesOptional = bubblesOptional;
mAssistManagerLazy = assistManagerLazy;
mRemoteInputManager = remoteInputManager;
mGroupMembershipManager = groupMembershipManager;
@@ -725,7 +729,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mStatusBarKeyguardViewManager,
mKeyguardManager,
mDreamManager,
- mBubbleController,
+ mBubblesOptional,
mAssistManagerLazy,
mRemoteInputManager,
mGroupMembershipManager,
@@ -736,12 +740,10 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mLockPatternUtils,
mRemoteInputCallback,
mActivityIntentHelper,
-
mFeatureFlags,
mMetricsLogger,
mLogger,
mOnUserInteractionCallback,
-
mStatusBar,
mNotificationPresenter,
mNotificationPanelViewController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index b7f83145f477..6d4099b656cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -31,7 +31,7 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.InitController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -43,7 +43,6 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
@@ -156,7 +155,7 @@ public interface StatusBarPhoneModule {
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
VibratorHelper vibratorHelper,
- BubbleController bubbleController,
+ Optional<Bubbles> bubblesOptional,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
@@ -175,7 +174,6 @@ public interface StatusBarPhoneModule {
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
- Optional<Recents> recentsOptional,
Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
Optional<SplitScreen> splitScreenOptional,
@@ -235,7 +233,7 @@ public interface StatusBarPhoneModule {
wakefulnessLifecycle,
statusBarStateController,
vibratorHelper,
- bubbleController,
+ bubblesOptional,
visualStabilityManager,
deviceProvisionedController,
navigationBarController,
@@ -254,7 +252,6 @@ public interface StatusBarPhoneModule {
dozeScrimController,
volumeComponent,
commandQueue,
- recentsOptional,
statusBarComponentBuilder,
pluginManager,
splitScreenOptional,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
index 7a78c157e5b4..0bd36240a366 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
@@ -58,6 +58,10 @@ public class TvNotificationPanel extends SystemUI implements CommandQueue.Callba
if (!mNotificationHandlerPackage.isEmpty()) {
startNotificationHandlerActivity(
new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL));
+ } else {
+ Log.w(TAG,
+ "Not toggling notification panel: config_notificationHandlerPackage is "
+ + "empty");
}
}
@@ -66,6 +70,10 @@ public class TvNotificationPanel extends SystemUI implements CommandQueue.Callba
if (!mNotificationHandlerPackage.isEmpty()) {
startNotificationHandlerActivity(
new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL));
+ } else {
+ Log.w(TAG,
+ "Not expanding notification panel: config_notificationHandlerPackage is "
+ + "empty");
}
}
@@ -77,6 +85,9 @@ public class TvNotificationPanel extends SystemUI implements CommandQueue.Callba
NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL);
closeNotificationIntent.setPackage(mNotificationHandlerPackage);
mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT);
+ } else {
+ Log.w(TAG,
+ "Not closing notification panel: config_notificationHandlerPackage is empty");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index eb8f065149c8..a6cd350b33ce 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -23,7 +23,6 @@ import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
-import com.android.keyguard.KeyguardMessageArea;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.qs.QSFooterImpl;
import com.android.systemui.qs.QSPanel;
@@ -108,11 +107,6 @@ public class InjectionInflationController {
NotificationStackScrollLayout createNotificationStackScrollLayout();
/**
- * Creates the KeyguardMessageArea.
- */
- KeyguardMessageArea createKeyguardMessageArea();
-
- /**
* Creates the QSPanel.
*/
QSPanel createQSPanel();
diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
index 64f8dbbb9e34..c7aa780fcacb 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
@@ -23,7 +23,20 @@ import android.view.View.OnAttachStateChangeListener;
* Utility class that handles view lifecycle events for View Controllers.
*
* Implementations should handle setup and teardown related activities inside of
- * {@link #onViewAttached()} and {@link #onViewDetached()}.
+ * {@link #onViewAttached()} and {@link #onViewDetached()}. Be sure to call {@link #init()} on
+ * any child controllers that this uses. This can be done in {@link init()} if the controllers
+ * are injected, or right after creation time of the child controller.
+ *
+ * Tip: View "attachment" happens top down - parents are notified that they are attached before
+ * any children. That means that if you call a method on a child controller in
+ * {@link #onViewAttached()}, the child controller may not have had its onViewAttach method
+ * called, so it may not be fully set up.
+ *
+ * As such, make sure that methods on your controller are safe to call _before_ its {@link #init()}
+ * and {@link #onViewAttached()} methods are called. Specifically, if your controller must call
+ * {@link View#findViewById(int)} on its root view to setup member variables, do so in its
+ * constructor. Save {@link #onViewAttached()} for things that can happen post-construction - adding
+ * listeners, dynamically changing content, or other runtime decisions.
*
* @param <T> View class that this ViewController is for.
*/
@@ -54,10 +67,12 @@ public abstract class ViewController<T extends View> {
}
mInited = true;
- if (mView.isAttachedToWindow()) {
- mOnAttachStateListener.onViewAttachedToWindow(mView);
+ if (mView != null) {
+ if (mView.isAttachedToWindow()) {
+ mOnAttachStateListener.onViewAttachedToWindow(mView);
+ }
+ mView.addOnAttachStateChangeListener(mOnAttachStateListener);
}
- mView.addOnAttachStateChangeListener(mOnAttachStateListener);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index d6595b2323bf..1e6a9e8dc007 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -26,11 +26,14 @@ import com.android.systemui.pip.tv.dagger.TvPipModule;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import java.util.concurrent.Executor;
+
import dagger.Module;
import dagger.Provides;
@@ -44,16 +47,18 @@ public class TvWMShellModule {
@SysUISingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
- DisplayController displayController, @Main Handler mainHandler,
+ DisplayController displayController, @Main Executor mainExecutor,
TransactionPool transactionPool) {
- return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
+ return new DisplayImeController(wmService, displayController, mainExecutor,
+ transactionPool);
}
static SplitScreen provideSplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController displayImeController, @Main Handler handler,
- TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer) {
+ TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue) {
return new SplitScreenController(context, displayController, systemWindows,
- displayImeController, handler, transactionPool, shellTaskOrganizer);
+ displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index ae96829379e9..dfb30b4652b9 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -24,6 +24,7 @@ import android.util.DisplayMetrics;
import android.view.IWindowManager;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.pip.Pip;
@@ -38,6 +39,7 @@ import com.android.systemui.util.FloatingContentCoordinator;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.FlingAnimationUtils;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.onehanded.OneHanded;
@@ -122,8 +124,15 @@ public abstract class WMShellBaseModule {
@SysUISingleton
@Provides
- static ShellTaskOrganizer provideShellTaskOrganizer(TransactionPool transactionPool) {
- ShellTaskOrganizer organizer = new ShellTaskOrganizer(transactionPool);
+ static SyncTransactionQueue provideSyncTransactionQueue(@Main Handler handler,
+ TransactionPool pool) {
+ return new SyncTransactionQueue(pool, handler);
+ }
+
+ @SysUISingleton
+ @Provides
+ static ShellTaskOrganizer provideShellTaskOrganizer(SyncTransactionQueue syncQueue) {
+ ShellTaskOrganizer organizer = new ShellTaskOrganizer(syncQueue);
organizer.registerOrganizer();
return organizer;
}
@@ -148,5 +157,8 @@ public abstract class WMShellBaseModule {
abstract SplitScreen optionalSplitScreen();
@BindsOptionalOf
+ abstract Bubbles optionalBubbles();
+
+ @BindsOptionalOf
abstract OneHanded optionalOneHanded();
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 6ed836c3b954..b4852b28619c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -36,6 +36,7 @@ import com.android.systemui.util.FloatingContentCoordinator;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.onehanded.OneHanded;
@@ -44,6 +45,7 @@ import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.util.Optional;
+import java.util.concurrent.Executor;
import dagger.Module;
import dagger.Provides;
@@ -58,9 +60,10 @@ public class WMShellModule {
@SysUISingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
- DisplayController displayController, @Main Handler mainHandler,
+ DisplayController displayController, @Main Executor mainExecutor,
TransactionPool transactionPool) {
- return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
+ return new DisplayImeController(wmService, displayController, mainExecutor,
+ transactionPool);
}
@SysUISingleton
@@ -84,9 +87,10 @@ public class WMShellModule {
static SplitScreen provideSplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController displayImeController, @Main Handler handler,
- TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer) {
+ TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue) {
return new SplitScreenController(context, displayController, systemWindows,
- displayImeController, handler, transactionPool, shellTaskOrganizer);
+ displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue);
}
@SysUISingleton
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index 9be2d124026c..dffad6ccbea5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -41,8 +41,6 @@ import android.testing.TestableLooper.RunWithLooper;
import android.testing.ViewUtils;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceView;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
@@ -67,7 +65,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
private ComponentName mComponentName;
private Intent mServiceIntent;
private TestableLooper mTestableLooper;
- private ViewGroup mParent;
+ private KeyguardSecurityContainer mKeyguardSecurityContainer;
@Mock
private Handler mHandler;
@@ -84,8 +82,8 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
- mParent = spy(new FrameLayout(mContext));
- ViewUtils.attachView(mParent);
+ mKeyguardSecurityContainer = spy(new KeyguardSecurityContainer(mContext));
+ ViewUtils.attachView(mKeyguardSecurityContainer);
mTestableLooper = TestableLooper.get(this);
mComponentName = new ComponentName(mContext, "FakeKeyguardClient.class");
@@ -96,13 +94,14 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
when(mKeyguardClient.queryLocalInterface(anyString())).thenReturn(mKeyguardClient);
when(mKeyguardClient.asBinder()).thenReturn(mKeyguardClient);
- mTestController = new AdminSecondaryLockScreenController(
- mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler);
+ mTestController = new AdminSecondaryLockScreenController.Factory(
+ mContext, mKeyguardSecurityContainer, mUpdateMonitor, mHandler)
+ .create(mKeyguardCallback);
}
@After
public void tearDown() {
- ViewUtils.detachView(mParent);
+ ViewUtils.detachView(mKeyguardSecurityContainer);
}
@Test
@@ -146,7 +145,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
SurfaceView v = verifySurfaceReady();
mTestController.hide();
- verify(mParent).removeView(v);
+ verify(mKeyguardSecurityContainer).removeView(v);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
@@ -154,7 +153,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
public void testHide_notShown() throws Exception {
mTestController.hide();
// Nothing should happen if trying to hide when the view isn't attached yet.
- verify(mParent, never()).removeView(any(SurfaceView.class));
+ verify(mKeyguardSecurityContainer, never()).removeView(any(SurfaceView.class));
}
@Test
@@ -182,7 +181,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
private SurfaceView verifySurfaceReady() throws Exception {
mTestableLooper.processAllMessages();
ArgumentCaptor<SurfaceView> captor = ArgumentCaptor.forClass(SurfaceView.class);
- verify(mParent).addView(captor.capture());
+ verify(mKeyguardSecurityContainer).addView(captor.capture());
mTestableLooper.processAllMessages();
verify(mKeyguardClient).onCreateKeyguardSurface(any(), any(IKeyguardCallback.class));
@@ -190,7 +189,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
}
private void verifyViewDismissed(SurfaceView v) throws Exception {
- verify(mParent).removeView(v);
+ verify(mKeyguardSecurityContainer).removeView(v);
verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
new file mode 100644
index 000000000000..c2ade81a9877
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.KeyEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
+
+ @Mock
+ private KeyguardAbsKeyInputView mAbsKeyInputView;
+ @Mock
+ private PasswordTextView mPasswordEntry;
+ @Mock
+ private KeyguardMessageArea mKeyguardMessageArea;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private SecurityMode mSecurityMode;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ @Mock
+ private KeyguardSecurityCallback mKeyguardSecurityCallback;
+ @Mock
+ private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
+ @Mock
+ private KeyguardMessageAreaController mKeyguardMessageAreaController;
+ @Mock
+ private LatencyTracker mLatencyTracker;
+
+ private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
+ .thenReturn(mKeyguardMessageAreaController);
+ when(mAbsKeyInputView.getPasswordTextViewId()).thenReturn(1);
+ when(mAbsKeyInputView.findViewById(1)).thenReturn(mPasswordEntry);
+ when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true);
+ when(mAbsKeyInputView.findViewById(R.id.keyguard_message_area))
+ .thenReturn(mKeyguardMessageArea);
+ mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
+ mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
+ mKeyguardMessageAreaControllerFactory, mLatencyTracker) {
+ @Override
+ void resetState() {
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ }
+ };
+ mKeyguardAbsKeyInputViewController.init();
+ reset(mKeyguardMessageAreaController); // Clear out implicit call to init.
+ }
+
+ @Test
+ public void onKeyDown_clearsSecurityMessage() {
+ ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor =
+ ArgumentCaptor.forClass(KeyDownListener.class);
+ verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture());
+ onKeyDownListenerArgumentCaptor.getValue().onKeyDown(
+ KeyEvent.KEYCODE_0, mock(KeyEvent.class));
+ verify(mKeyguardSecurityCallback).userActivity();
+ verify(mKeyguardMessageAreaController).setMessage(eq(""));
+ }
+
+ @Test
+ public void onKeyDown_noSecurityMessageInteraction() {
+ ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor =
+ ArgumentCaptor.forClass(KeyDownListener.class);
+ verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture());
+ onKeyDownListenerArgumentCaptor.getValue().onKeyDown(
+ KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class));
+ verifyZeroInteractions(mKeyguardSecurityCallback);
+ verifyZeroInteractions(mKeyguardMessageAreaController);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 5999e2cdec78..e7930795c7f8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -41,11 +41,9 @@ import android.widget.FrameLayout;
import android.widget.TextClock;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Test;
@@ -78,12 +76,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area))
.thenReturn(mMockKeyguardSliceView);
- InjectionInflationController inflationController = new InjectionInflationController(
- SystemUIFactory.getInstance()
- .getSysUIComponent()
- .createViewInstanceCreatorFactory());
- LayoutInflater layoutInflater = inflationController
- .injectable(LayoutInflater.from(getContext()));
+ LayoutInflater layoutInflater = LayoutInflater.from(getContext());
layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
index 54e879e2ff38..64632afe9bfa 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
@@ -16,8 +16,10 @@
package com.android.keyguard;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.media.AudioManager;
import android.telephony.TelephonyManager;
@@ -47,13 +49,15 @@ public class KeyguardHostViewControllerTest extends SysuiTestCase {
@Mock
private KeyguardHostView mKeyguardHostView;
@Mock
- private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
- @Mock
private AudioManager mAudioManager;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private ViewMediatorCallback mViewMediatorCallback;
+ @Mock
+ KeyguardSecurityContainerController.Factory mKeyguardSecurityContainerControllerFactory;
+ @Mock
+ private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -62,9 +66,12 @@ public class KeyguardHostViewControllerTest extends SysuiTestCase {
@Before
public void setup() {
+ when(mKeyguardSecurityContainerControllerFactory.create(any(
+ KeyguardSecurityContainer.SecurityCallback.class)))
+ .thenReturn(mKeyguardSecurityContainerController);
mKeyguardHostViewController = new KeyguardHostViewController(
- mKeyguardHostView, mKeyguardUpdateMonitor, mKeyguardSecurityContainerController,
- mAudioManager, mTelephonyManager, mViewMediatorCallback);
+ mKeyguardHostView, mKeyguardUpdateMonitor, mAudioManager, mTelephonyManager,
+ mViewMediatorCallback, mKeyguardSecurityContainerControllerFactory);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
new file mode 100644
index 000000000000..a7197cca530c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.keyguard;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
+ @Mock
+ private ConfigurationController mConfigurationController;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private KeyguardMessageArea mKeyguardMessageArea;
+
+ private KeyguardMessageAreaController mMessageAreaController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mMessageAreaController = new KeyguardMessageAreaController.Factory(
+ mKeyguardUpdateMonitor, mConfigurationController).create(mKeyguardMessageArea);
+ }
+
+ @Test
+ public void onAttachedToWindow_registersConfigurationCallback() {
+ ArgumentCaptor<ConfigurationListener> configurationListenerArgumentCaptor =
+ ArgumentCaptor.forClass(ConfigurationListener.class);
+
+ mMessageAreaController.onViewAttached();
+ verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+
+ mMessageAreaController.onViewDetached();
+ verify(mConfigurationController).removeCallback(
+ eq(configurationListenerArgumentCaptor.getValue()));
+ }
+
+ @Test
+ public void onAttachedToWindow_registersKeyguardUpdateMontiorCallback() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+
+ mMessageAreaController.onViewAttached();
+ verify(mKeyguardUpdateMonitor).registerCallback(
+ keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+
+ mMessageAreaController.onViewDetached();
+ verify(mKeyguardUpdateMonitor).removeCallback(
+ eq(keyguardUpdateMonitorCallbackArgumentCaptor.getValue()));
+ }
+
+ @Test
+ public void testClearsTextField() {
+ mMessageAreaController.setMessage("");
+ verify(mKeyguardMessageArea).setMessage("");
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
index fc7b9a4b47d1..31fb25a7a89c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,65 +11,60 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package com.android.keyguard;
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
+import static com.google.common.truth.Truth.assertThat;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ConfigurationController;
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
public class KeyguardMessageAreaTest extends SysuiTestCase {
- @Mock
- private ConfigurationController mConfigurationController;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private KeyguardMessageArea mMessageArea;
+ private KeyguardMessageArea mKeyguardMessageArea;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mMessageArea = new KeyguardMessageArea(mContext, null, mKeyguardUpdateMonitor,
- mConfigurationController);
- waitForIdleSync();
+ mKeyguardMessageArea = new KeyguardMessageArea(mContext, null);
+ mKeyguardMessageArea.setBouncerVisible(true);
}
@Test
- public void onAttachedToWindow_registersConfigurationCallback() {
- mMessageArea.onAttachedToWindow();
- verify(mConfigurationController).addCallback(eq(mMessageArea));
-
- mMessageArea.onDetachedFromWindow();
- verify(mConfigurationController).removeCallback(eq(mMessageArea));
+ public void testShowsTextField() {
+ mKeyguardMessageArea.setVisibility(View.INVISIBLE);
+ mKeyguardMessageArea.setMessage("oobleck");
+ assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck");
}
@Test
- public void clearFollowedByMessage_keepsMessage() {
- mMessageArea.setMessage("");
- mMessageArea.setMessage("test");
-
- CharSequence[] messageText = new CharSequence[1];
- messageText[0] = mMessageArea.getText();
-
- assertEquals("test", messageText[0]);
+ public void testHiddenWhenBouncerHidden() {
+ mKeyguardMessageArea.setBouncerVisible(false);
+ mKeyguardMessageArea.setVisibility(View.INVISIBLE);
+ mKeyguardMessageArea.setMessage("oobleck");
+ assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
+ assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck");
}
+ @Test
+ public void testClearsTextField() {
+ mKeyguardMessageArea.setVisibility(View.VISIBLE);
+ mKeyguardMessageArea.setMessage("");
+ assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
+ assertThat(mKeyguardMessageArea.getText()).isEqualTo("");
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
new file mode 100644
index 000000000000..c69ec1a254c3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.util.LatencyTracker
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternView
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class KeyguardPatternViewControllerTest : SysuiTestCase() {
+ @Mock
+ private lateinit var mKeyguardPatternView: KeyguardPatternView
+ @Mock
+ private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock
+ private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode
+ @Mock
+ private lateinit var mLockPatternUtils: LockPatternUtils
+ @Mock
+ private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
+ @Mock
+ private lateinit var mLatencyTracker: LatencyTracker
+ @Mock
+ private lateinit
+ var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
+ @Mock
+ private lateinit var mKeyguardMessageArea: KeyguardMessageArea
+ @Mock
+ private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
+ @Mock
+ private lateinit var mLockPatternView: LockPatternView
+
+ private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ `when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true)
+ `when`(mKeyguardPatternView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area))
+ .thenReturn(mKeyguardMessageArea)
+ `when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView))
+ .thenReturn(mLockPatternView)
+ `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea))
+ .thenReturn(mKeyguardMessageAreaController)
+ mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView,
+ mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
+ mLatencyTracker, mKeyguardMessageAreaControllerFactory)
+ }
+
+ @Test
+ fun onPause_clearsTextField() {
+ mKeyguardPatternViewController.init()
+ mKeyguardPatternViewController.onPause()
+ verify(mKeyguardMessageAreaController).setMessage("")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt
deleted file mode 100644
index b4363cf215f1..000000000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.keyguard
-
-import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.LayoutInflater
-
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.google.common.truth.Truth.assertThat
-
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class KeyguardPatternViewTest : SysuiTestCase() {
-
- private lateinit var mKeyguardPatternView: KeyguardPatternView
- private lateinit var mSecurityMessage: KeyguardMessageArea
-
- @Before
- fun setup() {
- val inflater = LayoutInflater.from(context)
- mDependency.injectMockDependency(KeyguardUpdateMonitor::class.java)
- mKeyguardPatternView = inflater.inflate(R.layout.keyguard_pattern_view, null)
- as KeyguardPatternView
- mSecurityMessage = KeyguardMessageArea(mContext, null,
- mock(KeyguardUpdateMonitor::class.java), mock(ConfigurationController::class.java))
- mKeyguardPatternView.mSecurityMessageDisplay = mSecurityMessage
- }
-
- @Test
- fun onPause_clearsTextField() {
- mSecurityMessage.setMessage("an old message")
- mKeyguardPatternView.onPause()
- assertThat(mSecurityMessage.text).isEqualTo("")
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
new file mode 100644
index 000000000000..4944284698a0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.keyguard;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+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
+public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
+
+ @Mock
+ private KeyguardPinBasedInputView mPinBasedInputView;
+ @Mock
+ private PasswordTextView mPasswordEntry;
+ @Mock
+ private KeyguardMessageArea mKeyguardMessageArea;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private SecurityMode mSecurityMode;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ @Mock
+ private KeyguardSecurityCallback mKeyguardSecurityCallback;
+ @Mock
+ private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
+ @Mock
+ private KeyguardMessageAreaController mKeyguardMessageAreaController;
+ @Mock
+ private LatencyTracker mLatencyTracker;
+ @Mock
+ private LiftToActivateListener mLiftToactivateListener;
+ @Mock
+ private View mDeleteButton;
+ @Mock
+ private View mOkButton;
+
+ private KeyguardPinBasedInputViewController mKeyguardPinViewController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
+ .thenReturn(mKeyguardMessageAreaController);
+ when(mPinBasedInputView.getPasswordTextViewId()).thenReturn(1);
+ when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry);
+ when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true);
+ when(mPinBasedInputView.findViewById(R.id.keyguard_message_area))
+ .thenReturn(mKeyguardMessageArea);
+ when(mPinBasedInputView.findViewById(R.id.delete_button))
+ .thenReturn(mDeleteButton);
+ when(mPinBasedInputView.findViewById(R.id.key_enter))
+ .thenReturn(mOkButton);
+ mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
+ mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
+ mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener) {
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ }
+ };
+ mKeyguardPinViewController.init();
+ }
+
+ @Test
+ public void onResume_requestsFocus() {
+ mKeyguardPinViewController.onResume(KeyguardSecurityView.SCREEN_ON);
+ verify(mPasswordEntry).requestFocus();
+ }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
deleted file mode 100644
index 6666a926c68b..000000000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.keyguard;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-public class KeyguardPinBasedInputViewTest extends SysuiTestCase {
-
- @Mock
- private PasswordTextView mPasswordEntry;
- @Mock
- private SecurityMessageDisplay mSecurityMessageDisplay;
- @InjectMocks
- private KeyguardPinBasedInputView mKeyguardPinView;
-
- @Before
- public void setup() {
- LayoutInflater inflater = LayoutInflater.from(getContext());
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
- mKeyguardPinView =
- (KeyguardPinBasedInputView) inflater.inflate(R.layout.keyguard_pin_view, null);
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void onResume_requestsFocus() {
- mKeyguardPinView.onResume(KeyguardSecurityView.SCREEN_ON);
- verify(mPasswordEntry).requestFocus();
- }
-
- @Test
- public void onKeyDown_clearsSecurityMessage() {
- mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_0, mock(KeyEvent.class));
- verify(mSecurityMessageDisplay).setMessage(eq(""));
- }
-
- @Test
- public void onKeyDown_noSecurityMessageInteraction() {
- mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class));
- verifyZeroInteractions(mSecurityMessageDisplay);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index 559284ac0672..ae159c73b99f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -31,9 +31,7 @@ import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardDisplayManager.KeyguardPresentation;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.InjectionInflationController;
import org.junit.After;
import org.junit.Before;
@@ -65,7 +63,6 @@ public class KeyguardPresentationTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
when(mMockKeyguardClockSwitch.getContext()).thenReturn(mContext);
when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
when(mMockKeyguardStatusView.getContext()).thenReturn(mContext);
@@ -77,11 +74,7 @@ public class KeyguardPresentationTest extends SysuiTestCase {
allowTestableLooperAsMainThread();
- InjectionInflationController inflationController = new InjectionInflationController(
- SystemUIFactory.getInstance()
- .getSysUIComponent()
- .createViewInstanceCreatorFactory());
- mLayoutInflater = inflationController.injectable(LayoutInflater.from(mContext));
+ mLayoutInflater = LayoutInflater.from(mContext);
mLayoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
new file mode 100644
index 000000000000..eef38d316775
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.keyguard;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.WindowInsetsController;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper()
+public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
+
+ @Rule
+ public MockitoRule mRule = MockitoJUnit.rule();
+
+ @Mock
+ private KeyguardSecurityContainer mView;
+ @Mock
+ private AdminSecondaryLockScreenController.Factory mAdminSecondaryLockScreenControllerFactory;
+ @Mock
+ private AdminSecondaryLockScreenController mAdminSecondaryLockScreenController;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private KeyguardSecurityModel mKeyguardSecurityModel;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private UiEventLogger mUiEventLogger;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private KeyguardInputViewController mInputViewController;
+ @Mock
+ private KeyguardSecurityContainer.SecurityCallback mSecurityCallback;
+ @Mock
+ private WindowInsetsController mWindowInsetsController;
+ @Mock
+ private KeyguardSecurityViewFlipper mSecurityViewFlipper;
+ @Mock
+ private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
+
+ private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
+
+ @Before
+ public void setup() {
+ when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
+ .thenReturn(mAdminSecondaryLockScreenController);
+ when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+
+ mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory(
+ mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
+ mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
+ mKeyguardStateController, mKeyguardSecurityViewFlipperController)
+ .create(mSecurityCallback);
+ }
+
+ @Test
+ public void showSecurityScreen_canInflateAllModes() {
+ SecurityMode[] modes = SecurityMode.values();
+ for (SecurityMode mode : modes) {
+ when(mInputViewController.getSecurityMode()).thenReturn(mode);
+ mKeyguardSecurityContainerController.showSecurityScreen(mode);
+ if (mode == SecurityMode.Invalid) {
+ verify(mKeyguardSecurityViewFlipperController, never()).getSecurityView(
+ any(SecurityMode.class), any(KeyguardSecurityCallback.class));
+ } else {
+ verify(mKeyguardSecurityViewFlipperController).getSecurityView(
+ eq(mode), any(KeyguardSecurityCallback.class));
+ }
+ }
+ }
+
+ @Test
+ public void startDisappearAnimation_animatesKeyboard() {
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ SecurityMode.Password);
+ when(mInputViewController.getSecurityMode()).thenReturn(
+ SecurityMode.Password);
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
+ .thenReturn(mInputViewController);
+ mKeyguardSecurityContainerController.showPrimarySecurityScreen(false /* turningOff */);
+
+ mKeyguardSecurityContainerController.startDisappearAnimation(null);
+ verify(mInputViewController).startDisappearAnimation(eq(null));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index a867825e223d..854be1f76722 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -19,23 +19,19 @@ package com.android.keyguard;
import static android.view.WindowInsets.Type.ime;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.LayoutInflater;
import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Rule;
@@ -50,68 +46,26 @@ import org.mockito.junit.MockitoRule;
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerTest extends SysuiTestCase {
- @Mock
- private KeyguardSecurityModel mKeyguardSecurityModel;
- @Mock
- private KeyguardStateController mKeyguardStateController;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- private KeyguardSecurityContainer.SecurityCallback mSecurityCallback;
- @Mock
- private KeyguardSecurityView mSecurityView;
+ @Rule
+ public MockitoRule mRule = MockitoJUnit.rule();
+
@Mock
private WindowInsetsController mWindowInsetsController;
@Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
- @Rule
- public MockitoRule mRule = MockitoJUnit.rule();
+
private KeyguardSecurityContainer mKeyguardSecurityContainer;
@Before
public void setup() {
- mDependency.injectTestDependency(KeyguardStateController.class, mKeyguardStateController);
- mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel);
- mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
- mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext()) {
- @Override
- protected KeyguardSecurityView getSecurityView(
- KeyguardSecurityModel.SecurityMode securityMode) {
- return mSecurityView;
- }
- };
- mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
- mKeyguardSecurityContainer.setSecurityCallback(mSecurityCallback);
- }
-
- @Test
- public void showSecurityScreen_canInflateAllModes() {
- Context context = getContext();
-
- for (int theme : new int[] {R.style.Theme_SystemUI, R.style.Theme_SystemUI_Light}) {
- context.setTheme(theme);
- final LayoutInflater inflater = LayoutInflater.from(context);
- KeyguardSecurityModel.SecurityMode[] modes =
- KeyguardSecurityModel.SecurityMode.values();
- for (KeyguardSecurityModel.SecurityMode mode : modes) {
- final int resId = mKeyguardSecurityContainer.getLayoutIdFor(mode);
- if (resId == 0) {
- continue;
- }
- inflater.inflate(resId, null /* root */, false /* attach */);
- }
- }
+ mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
+ mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
}
@Test
public void startDisappearAnimation_animatesKeyboard() {
- when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
- KeyguardSecurityModel.SecurityMode.Password);
- mKeyguardSecurityContainer.showPrimarySecurityScreen(false /* turningOff */);
-
- mKeyguardSecurityContainer.startDisappearAnimation(null);
- verify(mSecurityView).startDisappearAnimation(eq(null));
+ mKeyguardSecurityContainer.startDisappearAnimation(SecurityMode.Password);
verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(),
any(), any());
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
new file mode 100644
index 000000000000..3b7f4b839853
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.keyguard;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.view.WindowInsetsController;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper()
+public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
+
+ @Rule
+ public MockitoRule mRule = MockitoJUnit.rule();
+
+ @Mock
+ private KeyguardSecurityViewFlipper mView;
+ @Mock
+ private LayoutInflater mLayoutInflater;
+ @Mock
+ private KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory;
+ @Mock
+ private KeyguardInputViewController mKeyguardInputViewController;
+ @Mock
+ private KeyguardInputView mInputView;
+ @Mock
+ private WindowInsetsController mWindowInsetsController;
+ @Mock
+ private KeyguardSecurityCallback mKeyguardSecurityCallback;
+
+ private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
+
+ @Before
+ public void setup() {
+ when(mKeyguardSecurityViewControllerFactory.create(
+ any(KeyguardInputView.class), any(SecurityMode.class),
+ any(KeyguardSecurityCallback.class)))
+ .thenReturn(mKeyguardInputViewController);
+ when(mView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+
+ mKeyguardSecurityViewFlipperController = new KeyguardSecurityViewFlipperController(mView,
+ mLayoutInflater, mKeyguardSecurityViewControllerFactory);
+ }
+
+ @Test
+ public void showSecurityScreen_canInflateAllModes() {
+ SecurityMode[] modes = SecurityMode.values();
+ // Always return an invalid controller so that we're always making a new one.
+ when(mKeyguardInputViewController.getSecurityMode()).thenReturn(SecurityMode.Invalid);
+ for (SecurityMode mode : modes) {
+ reset(mLayoutInflater);
+ when(mLayoutInflater.inflate(anyInt(), eq(mView), eq(false)))
+ .thenReturn(mInputView);
+ mKeyguardSecurityViewFlipperController.getSecurityView(mode, mKeyguardSecurityCallback);
+ if (mode == SecurityMode.Invalid || mode == SecurityMode.None) {
+ verify(mLayoutInflater, never()).inflate(
+ anyInt(), any(ViewGroup.class), anyBoolean());
+ } else {
+ verify(mLayoutInflater).inflate(anyInt(), eq(mView), eq(false));
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
index 0431704778c3..79ec4f2c553a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
@@ -24,9 +24,7 @@ import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Test;
@@ -50,13 +48,7 @@ public class KeyguardStatusViewTest extends SysuiTestCase {
@Before
public void setUp() {
allowTestableLooperAsMainThread();
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
- InjectionInflationController inflationController = new InjectionInflationController(
- SystemUIFactory.getInstance()
- .getSysUIComponent()
- .createViewInstanceCreatorFactory());
- LayoutInflater layoutInflater = inflationController
- .injectable(LayoutInflater.from(getContext()));
+ LayoutInflater layoutInflater = LayoutInflater.from(getContext());
mKeyguardStatusView =
(KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null);
org.mockito.MockitoAnnotations.initMocks(this);
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 9b9f840e5383..7df9f1fff844 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics;
+import static junit.framework.Assert.assertEquals;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
@@ -25,7 +27,9 @@ import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -54,11 +58,18 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.ArrayList;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class UdfpsControllerTest extends SysuiTestCase {
+ // Use this for inputs going into SystemUI. Use UdfpsController.mUdfpsSensorId for things
+ // leaving SystemUI.
+ private static final int TEST_UDFPS_SENSOR_ID = 1;
+
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@@ -98,6 +109,13 @@ public class UdfpsControllerTest extends SysuiTestCase {
public void setUp() {
setUpResources();
when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)).thenReturn(mUdfpsView);
+ final List<FingerprintSensorProperties> props = new ArrayList<>();
+ props.add(new FingerprintSensorProperties(TEST_UDFPS_SENSOR_ID,
+ SensorProperties.STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */,
+ FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+ true /* resetLockoutRequiresHardwareAuthToken */));
+ when(mFingerprintManager.getSensorProperties()).thenReturn(props);
mSystemSettings = new FakeSettings();
mFgExecutor = new FakeExecutor(new FakeSystemClock());
mUdfpsController = new UdfpsController(
@@ -112,6 +130,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
mFgExecutor);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
+
+ assertEquals(TEST_UDFPS_SENSOR_ID, mUdfpsController.mUdfpsSensorId);
}
private void setUpResources() {
@@ -138,15 +158,15 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void showUdfpsOverlay_addsViewToWindow() throws RemoteException {
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
verify(mWindowManager).addView(eq(mUdfpsView), any());
}
@Test
public void hideUdfpsOverlay_removesViewFromWindow() throws RemoteException {
- mOverlayController.showUdfpsOverlay();
- mOverlayController.hideUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
+ mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
verify(mWindowManager).removeView(eq(mUdfpsView));
}
@@ -156,7 +176,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// GIVEN that the bouncer is showing
mUdfpsController.setBouncerVisibility(/* isShowing */ true);
// WHEN a request to show the overlay is received
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
// THEN the overlay is not attached
verify(mWindowManager, never()).addView(eq(mUdfpsView), any());
@@ -165,7 +185,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void setBouncerVisibility_overlayDetached() throws RemoteException {
// GIVEN that the overlay has been requested
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
// WHEN the bouncer becomes visible
mUdfpsController.setBouncerVisibility(/* isShowing */ true);
mFgExecutor.runAllReady();
@@ -178,7 +198,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// GIVEN that the bouncer is visible
mUdfpsController.setBouncerVisibility(/* isShowing */ true);
// AND the overlay has been requested
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
// WHEN the bouncer is closed
mUdfpsController.setBouncerVisibility(/* isShowing */ false);
mFgExecutor.runAllReady();
@@ -193,7 +213,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mUdfpsView.isValidTouch(anyFloat(), anyFloat(), anyFloat())).thenReturn(true);
// GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -201,7 +221,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
// THEN the event is passed to the FingerprintManager
- verify(mFingerprintManager).onFingerDown(eq(0), eq(0), eq(0f), eq(0f));
+ verify(mFingerprintManager).onFingerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
+ eq(0f), eq(0f));
// AND the scrim and dot is shown
verify(mUdfpsView).showScrimAndDot();
}
@@ -209,12 +230,13 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void aodInterrupt() throws RemoteException {
// GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
// WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0);
// THEN the event is passed to the FingerprintManager
- verify(mFingerprintManager).onFingerDown(eq(0), eq(0), anyFloat(), anyFloat());
+ verify(mFingerprintManager).onFingerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
+ anyFloat(), anyFloat());
// AND the scrim and dot is shown
verify(mUdfpsView).showScrimAndDot();
}
@@ -222,7 +244,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void cancelAodInterrupt() throws RemoteException {
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0);
// WHEN it is cancelled
@@ -234,7 +256,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void aodInterruptTimeout() throws RemoteException {
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0);
// WHEN it times out
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
index 3f10c8da576b..9b6dd05cd80c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
@@ -53,8 +53,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.navigationbar.buttons.KeyButtonView;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.recents.OverviewProxyService;
import org.junit.Before;
@@ -71,7 +70,7 @@ public class KeyButtonViewTest extends SysuiTestCase {
private KeyButtonView mKeyButtonView;
private MetricsLogger mMetricsLogger;
- private BubbleController mBubbleController;
+ private Bubbles mBubbles;
private UiEventLogger mUiEventLogger;
private InputManager mInputManager = mock(InputManager.class);
@Captor
@@ -81,7 +80,7 @@ public class KeyButtonViewTest extends SysuiTestCase {
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
- mBubbleController = mDependency.injectMockDependency(BubbleController.class);
+ mBubbles = mDependency.injectMockDependency(Bubbles.class);
mDependency.injectMockDependency(OverviewProxyService.class);
mUiEventLogger = mDependency.injectMockDependency(UiEventLogger.class);
@@ -155,7 +154,7 @@ public class KeyButtonViewTest extends SysuiTestCase {
@Test
public void testBubbleEvents_bubbleExpanded() {
- when(mBubbleController.getExpandedDisplayId(mContext)).thenReturn(3);
+ when(mBubbles.getExpandedDisplayId(mContext)).thenReturn(3);
int action = KeyEvent.ACTION_DOWN;
int flags = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index d041ee047ae0..10eca00feec4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -36,7 +36,7 @@ import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.DynamicChildBindController;
@@ -65,6 +65,7 @@ import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import java.util.List;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -106,7 +107,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
mock(StatusBarStateControllerImpl.class), mEntryManager,
mock(KeyguardBypassController.class),
- mock(BubbleController.class),
+ Optional.of(mock(Bubbles.class)),
mock(DynamicPrivacyController.class),
mock(ForegroundServiceSectionController.class),
mock(DynamicChildBindController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index dfe006dfd4fe..d835123e4cad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -42,6 +42,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -61,6 +62,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -112,7 +115,8 @@ public class NotificationFilterTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationGroupManagerLegacy.class,
new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
- () -> mock(PeopleNotificationIdentifier.class)));
+ () -> mock(PeopleNotificationIdentifier.class),
+ Optional.of(() -> mock(Bubbles.class))));
mDependency.injectMockDependency(ShadeController.class);
mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index edb8776bcb02..b20f95cfad3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -46,7 +46,6 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -78,7 +77,6 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
public void setUp() {
allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
- mDependency.injectMockDependency(BubbleController.class);
when(mGutsManager.openGuts(
any(View.class),
anyInt(),
@@ -86,7 +84,6 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
any(NotificationMenuRowPlugin.MenuItem.class)))
.thenReturn(true);
when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
- mDependency.injectMockDependency(BubbleController.class);
mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 3c5aa1ae9519..9465a3db00ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -76,7 +76,7 @@ import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.bubbles.BubblesTestActivity;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -97,6 +97,7 @@ import org.mockito.stubbing.Answer;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import javax.inject.Provider;
@@ -136,7 +137,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
@Mock
private OnUserInteractionCallback mOnUserInteractionCallback;
@Mock
- private BubbleController mBubbleController;
+ private Bubbles mBubbles;
@Mock
private LauncherApps mLauncherApps;
@Mock
@@ -161,7 +162,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mTestHandler = new Handler(mTestableLooper.getLooper());
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
- mDependency.injectTestDependency(BubbleController.class, mBubbleController);
mDependency.injectTestDependency(ShadeController.class, mShadeController);
// Inflate the layout
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
@@ -255,7 +255,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon);
assertEquals(mIconDrawable, view.getDrawable());
}
@@ -279,7 +279,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -330,7 +330,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
assertTrue(textView.getText().toString().contains(group.getName()));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -355,7 +355,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
assertEquals(GONE, textView.getVisibility());
@@ -379,7 +379,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
}
@@ -414,7 +414,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
assertTrue(nameView.getText().toString().contains("Proxied"));
@@ -442,7 +442,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -468,7 +468,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -495,7 +495,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
false,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -520,7 +520,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
View view = mNotificationInfo.findViewById(R.id.silence);
assertThat(view.isSelected()).isTrue();
}
@@ -548,7 +548,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
View view = mNotificationInfo.findViewById(R.id.default_behavior);
assertThat(view.isSelected()).isTrue();
assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
@@ -579,7 +579,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
View view = mNotificationInfo.findViewById(R.id.default_behavior);
assertThat(view.isSelected()).isTrue();
assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
@@ -609,7 +609,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -653,7 +653,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mTestableLooper.processAllMessages();
@@ -696,7 +696,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
View silence = mNotificationInfo.findViewById(R.id.silence);
@@ -740,7 +740,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -777,7 +777,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -813,7 +813,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -851,7 +851,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -887,7 +887,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -923,7 +923,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -958,7 +958,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
View silence = mNotificationInfo.findViewById(R.id.silence);
silence.performClick();
@@ -992,7 +992,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage(
anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
@@ -1017,7 +1017,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage(
anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
@@ -1052,7 +1052,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
() -> b,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
// WHEN user clicks "priority"
mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);
@@ -1092,7 +1092,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
() -> b,
true,
mTestHandler,
- mTestHandler, null, mBubbleController);
+ mTestHandler, null, Optional.of(mBubbles));
// WHEN user clicks "priority"
mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index e1668cab3333..bbc1df21237f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -67,7 +67,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -93,6 +93,8 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Optional;
+
import javax.inject.Provider;
/**
@@ -129,7 +131,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
@Mock private ChannelEditorDialogController mChannelEditorDialogController;
@Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@Mock private UserContextProvider mContextTracker;
- @Mock private BubbleController mBubbleController;
+ @Mock private Bubbles mBubbles;
@Mock(answer = Answers.RETURNS_SELF)
private PriorityOnboardingDialogController.Builder mBuilder;
private Provider<PriorityOnboardingDialogController.Builder> mProvider = () -> mBuilder;
@@ -145,7 +147,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
mDependency.injectTestDependency(
OnUserInteractionCallback.class,
mOnUserInteractionCallback);
- mDependency.injectTestDependency(BubbleController.class, mBubbleController);
mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mHandler = Handler.createAsync(mTestableLooper.getLooper());
mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
@@ -155,7 +156,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
() -> mStatusBar, mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider,
mINotificationManager, mLauncherApps, mShortcutManager,
mChannelEditorDialogController, mContextTracker, mProvider,
- mAssistantFeedbackController, mBubbleController,
+ mAssistantFeedbackController, Optional.of(mBubbles),
new UiEventLoggerFake(), mOnUserInteractionCallback);
mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
mCheckSaveListener, mOnSettingsClickListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index b952c056c33d..2ce8b34b193a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -45,7 +45,7 @@ import android.widget.RemoteViews;
import com.android.systemui.R;
import com.android.systemui.TestableDependency;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.bubbles.BubblesTestActivity;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.FalsingManager;
@@ -73,6 +73,7 @@ import com.android.systemui.statusbar.policy.SmartReplyConstants;
import org.mockito.ArgumentCaptor;
+import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -114,12 +115,12 @@ public class NotificationTestHelper {
mContext = context;
mTestLooper = testLooper;
dependency.injectMockDependency(NotificationMediaManager.class);
- dependency.injectMockDependency(BubbleController.class);
dependency.injectMockDependency(NotificationShadeWindowController.class);
mStatusBarStateController = mock(StatusBarStateController.class);
mGroupMembershipManager = new NotificationGroupManagerLegacy(
mStatusBarStateController,
- () -> mock(PeopleNotificationIdentifier.class));
+ () -> mock(PeopleNotificationIdentifier.class),
+ Optional.of(() -> mock(Bubbles.class)));
mGroupExpansionManager = mGroupMembershipManager;
mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController,
mock(KeyguardBypassController.class), mock(NotificationGroupManagerLegacy.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 57020eb08a7f..83b6d2c088b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -29,7 +29,6 @@ import android.view.View;
import androidx.test.filters.SmallTest;
-import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
@@ -95,7 +94,6 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
when(accessibilityMgr.getRecommendedTimeoutMillis(anyInt(), anyInt()))
.thenReturn(TEST_AUTO_DISMISS_TIME);
when(mVSManager.isReorderingAllowed()).thenReturn(true);
- mDependency.injectMockDependency(BubbleController.class);
mDependency.injectMockDependency(NotificationShadeWindowController.class);
mDependency.injectMockDependency(ConfigurationController.class);
mHeadsUpManager = new TestableHeadsUpManagerPhone(mContext, mGroupManager, mVSManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 2ece8be8d332..7d84f86cc7b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -37,7 +37,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -61,6 +61,7 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.HashMap;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -83,7 +84,6 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mDependency.injectMockDependency(BubbleController.class);
mHeadsUpManager = new HeadsUpManager(mContext) {};
when(mNotificationEntryManager.getPendingNotificationsIterator())
@@ -91,7 +91,8 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
mGroupManager = new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
- () -> mock(PeopleNotificationIdentifier.class));
+ () -> mock(PeopleNotificationIdentifier.class),
+ Optional.of(() -> mock(Bubbles.class)));
mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index 0aa009134440..29e445a13e24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -30,7 +30,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
@@ -45,6 +45,8 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Optional;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -60,14 +62,15 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
@Before
public void setup() {
- mDependency.injectMockDependency(BubbleController.class);
+ mDependency.injectMockDependency(Bubbles.class);
initializeGroupManager();
}
private void initializeGroupManager() {
mGroupManager = new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
- () -> mock(PeopleNotificationIdentifier.class));
+ () -> mock(PeopleNotificationIdentifier.class),
+ Optional.of(() -> mock(Bubbles.class)));
mGroupManager.setHeadsUpManager(mHeadsUpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
index 5222ffff2637..ede5fceb4ebd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
@@ -26,7 +26,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -41,6 +41,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -66,7 +68,7 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase {
StatusBarWindowController mStatusBarWindowController;
private NotificationIconAreaController mController;
@Mock
- private BubbleController mBubbleController;
+ private Bubbles mBubbles;
@Mock private DemoModeController mDemoModeController;
@Mock
private NotificationIconContainer mAodIcons;
@@ -83,7 +85,7 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase {
mNotificationMediaManager,
mListener,
mDozeParameters,
- mBubbleController,
+ Optional.of(mBubbles),
mDemoModeController,
mDarkIconDispatcher,
mStatusBarWindowController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 792637d8479b..f7489b1c164a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -53,7 +53,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -86,6 +86,7 @@ import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -115,7 +116,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private Handler mHandler;
@Mock
- private BubbleController mBubbleController;
+ private Bubbles mBubbles;
@Mock
private ShadeControllerImpl mShadeController;
@Mock
@@ -192,7 +193,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mStatusBarKeyguardViewManager,
mock(KeyguardManager.class),
mock(IDreamManager.class),
- mBubbleController,
+ Optional.of(mBubbles),
() -> mAssistManager,
mRemoteInputManager,
mock(NotificationGroupManagerLegacy.class),
@@ -279,7 +280,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
// Then
- verify(mBubbleController).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry()));
+ verify(mBubbles).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry()));
// This is called regardless, and simply short circuits when there is nothing to do.
verify(mShadeController, atLeastOnce()).collapsePanel();
@@ -311,7 +312,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
// Then
- verify(mBubbleController).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry());
+ verify(mBubbles).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry());
verify(mShadeController, atLeastOnce()).collapsePanel();
@@ -341,7 +342,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
// Then
- verify(mBubbleController).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry());
+ verify(mBubbles).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry());
verify(mShadeController, atLeastOnce()).collapsePanel();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 7d8a62607395..a6ea9966a0f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -80,7 +80,7 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.demomode.DemoModeController;
@@ -93,7 +93,6 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
@@ -219,7 +218,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private UserSwitcherController mUserSwitcherController;
@Mock private NetworkController mNetworkController;
@Mock private VibratorHelper mVibratorHelper;
- @Mock private BubbleController mBubbleController;
+ @Mock private Bubbles mBubbles;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
@@ -232,7 +231,6 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private KeyguardLiftController mKeyguardLiftController;
@Mock private VolumeComponent mVolumeComponent;
@Mock private CommandQueue mCommandQueue;
- @Mock private Recents mRecents;
@Mock private Provider<StatusBarComponent.Builder> mStatusBarComponentBuilderProvider;
@Mock private StatusBarComponent.Builder mStatusBarComponentBuilder;
@Mock private StatusBarComponent mStatusBarComponent;
@@ -332,7 +330,7 @@ public class StatusBarTest extends SysuiTestCase {
mShadeController = new ShadeControllerImpl(mCommandQueue,
mStatusBarStateController, mNotificationShadeWindowController,
mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
- () -> mStatusBar, () -> mAssistManager, () -> mBubbleController);
+ () -> mStatusBar, () -> mAssistManager, Optional.of(() -> mBubbles));
mStatusBar = new StatusBar(
mContext,
@@ -374,7 +372,7 @@ public class StatusBarTest extends SysuiTestCase {
wakefulnessLifecycle,
mStatusBarStateController,
mVibratorHelper,
- mBubbleController,
+ Optional.of(mBubbles),
mVisualStabilityManager,
mDeviceProvisionedController,
mNavigationBarController,
@@ -392,7 +390,6 @@ public class StatusBarTest extends SysuiTestCase {
mDozeScrimController,
mVolumeComponent,
mCommandQueue,
- Optional.of(mRecents),
mStatusBarComponentBuilderProvider,
mPluginManager,
Optional.of(mSplitScreen),
diff --git a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
index 73fc833fabf5..084743db03c4 100644
--- a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
+++ b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
@@ -25,7 +25,6 @@ import static android.system.OsConstants.SOCK_NONBLOCK;
import static android.system.OsConstants.SOCK_RAW;
import android.net.util.InterfaceParams;
-import android.net.util.PacketReader;
import android.net.util.SocketUtils;
import android.net.util.TetheringUtils;
import android.os.Handler;
@@ -33,6 +32,8 @@ import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
+import com.android.net.module.util.PacketReader;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.Inet6Address;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 7dd5290ee83b..64d5025807e7 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -2104,7 +2104,7 @@ public class Tethering {
}
private boolean hasCallingPermission(@NonNull String permission) {
- return mContext.checkCallingPermission(permission) == PERMISSION_GRANTED;
+ return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
}
/** Unregister tethering event callback */
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 64be2d9a5599..d206ea0b4d45 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -553,7 +553,6 @@ public class EthernetTetheringTest {
TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class);
TestNetworkInterface iface = tnm.createTapInterface();
Log.d(TAG, "Created test interface " + iface.getInterfaceName());
- assertNotNull(NetworkInterface.getByName(iface.getInterfaceName()));
return iface;
}
diff --git a/packages/overlays/IconShapePebbleOverlay/res/values/config.xml b/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
index 2465fe015538..e7eeb30501b5 100644
--- a/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
+++ b/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
@@ -18,7 +18,7 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
- <string name="config_icon_mask" translatable="false">"MM55,0 C25,0 0,25 0,50 0,78 28,100 55,100 85,100 100,85 100,58 100,30 86,0 55,0 Z"</string>
+ <string name="config_icon_mask" translatable="false">"M55,0 C25,0 0,25 0,50 0,78 28,100 55,100 85,100 100,85 100,58 100,30 86,0 55,0 Z"</string>
<!-- Flag indicating whether round icons should be parsed from the application manifest. -->
<bool name="config_useRoundIcon">false</bool>
<!-- Corner radius of system dialogs -->
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 80e9703e0e62..ae33f0c3da9c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -119,8 +119,8 @@ import com.android.internal.util.IntPair;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
+import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
-import com.android.server.accessibility.magnification.MagnificationTransitionController;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -219,16 +219,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// Lazily initialized - access through getSystemActionPerfomer()
private SystemActionPerformer mSystemActionPerformer;
- private FullScreenMagnificationController mFullScreenMagnificationController;
-
private InteractionBridge mInteractionBridge;
private AlertDialog mEnableTouchExplorationDialog;
private AccessibilityInputFilter mInputFilter;
- private WindowMagnificationManager mWindowMagnificationMgr;
-
private boolean mHasInputFilter;
private KeyEventDispatcher mKeyEventDispatcher;
@@ -259,7 +255,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private Point mTempPoint = new Point();
private boolean mIsAccessibilityButtonShown;
- private MagnificationTransitionController mMagnificationTransitionController;
+ private MagnificationController mMagnificationController;
private AccessibilityUserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
@@ -292,7 +288,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
SystemActionPerformer systemActionPerformer,
AccessibilityWindowManager a11yWindowManager,
AccessibilityDisplayListener a11yDisplayListener,
- WindowMagnificationManager windowMagnificationMgr) {
+ MagnificationController magnificationController) {
mContext = context;
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
@@ -303,8 +299,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mSystemActionPerformer = systemActionPerformer;
mA11yWindowManager = a11yWindowManager;
mA11yDisplayListener = a11yDisplayListener;
- mWindowMagnificationMgr = windowMagnificationMgr;
- mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
+ mMagnificationController = magnificationController;
init();
}
@@ -324,7 +319,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
- mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
+ mMagnificationController = new MagnificationController(this, mLock, mContext);
init();
}
@@ -1195,9 +1190,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// The user changed.
mCurrentUserId = userId;
- if (mWindowMagnificationMgr != null) {
- mWindowMagnificationMgr.setUserId(mCurrentUserId);
- }
+ mMagnificationController.updateUserIdIfNeeded(mCurrentUserId);
AccessibilityUserState userState = getCurrentUserStateLocked();
readConfigurationForUserStateLocked(userState);
@@ -1554,7 +1547,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (fallBackMagnificationModeSettingsLocked(userState)) {
return;
}
- mMagnificationTransitionController.transitionMagnificationModeLocked(
+ mMagnificationController.transitionMagnificationModeLocked(
Display.DEFAULT_DISPLAY, userState.getMagnificationModeLocked(),
this::onMagnificationTransitionEndedLocked);
}
@@ -2310,13 +2303,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return;
}
- if (mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.setUserId(userState.mUserId);
- }
-
if (mUiAutomationManager.suppressingAccessibilityServicesLocked()
- && mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.unregisterAll();
+ && mMagnificationController.isFullScreenMagnificationControllerInitialized()) {
+ getFullScreenMagnificationController().unregisterAll();
return;
}
@@ -2339,8 +2328,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final int displayId = display.getDisplayId();
if (userHasListeningMagnificationServicesLocked(userState, displayId)) {
getFullScreenMagnificationController().register(displayId);
- } else if (mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.unregister(displayId);
+ } else if (mMagnificationController.isFullScreenMagnificationControllerInitialized()) {
+ getFullScreenMagnificationController().unregister(displayId);
}
}
}
@@ -2990,10 +2979,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
public WindowMagnificationManager getWindowMagnificationMgr() {
synchronized (mLock) {
- if (mWindowMagnificationMgr == null) {
- mWindowMagnificationMgr = new WindowMagnificationManager(mContext, mCurrentUserId);
- }
- return mWindowMagnificationMgr;
+ return mMagnificationController.getWindowMagnificationMgr();
}
}
@@ -3060,12 +3046,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public FullScreenMagnificationController getFullScreenMagnificationController() {
synchronized (mLock) {
- if (mFullScreenMagnificationController == null) {
- mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
- this, mLock);
- mFullScreenMagnificationController.setUserId(mCurrentUserId);
- }
- return mFullScreenMagnificationController;
+ return mMagnificationController.getFullScreenMagnificationController();
}
}
@@ -3307,9 +3288,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
}
- if (mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.onDisplayRemoved(displayId);
- }
+ mMagnificationController.onDisplayRemoved(displayId);
mA11yWindowManager.stopTrackingWindows(displayId);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index af4b34f9613a..0dc5267c47f6 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -21,6 +21,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
@@ -29,21 +30,26 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.accessibility.MagnificationAnimationCallback;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilityManagerService;
/**
- * Handles magnification mode transition.
+ * Handles all magnification controllers initialization, generic interactions
+ * and magnification mode transition.
*/
-public class MagnificationTransitionController {
+public class MagnificationController {
private static final boolean DEBUG = false;
private static final String TAG = "MagnificationController";
private final AccessibilityManagerService mAms;
private final PointF mTempPoint = new PointF();
private final Object mLock;
+ private final Context mContext;
private final SparseArray<DisableMagnificationCallback>
mMagnificationEndRunnableSparseArray = new SparseArray();
+ private FullScreenMagnificationController mFullScreenMagnificationController;
+ private WindowMagnificationManager mWindowMagnificationMgr;
/**
* A callback to inform the magnification transition result.
@@ -56,9 +62,20 @@ public class MagnificationTransitionController {
void onResult(boolean success);
}
- public MagnificationTransitionController(AccessibilityManagerService ams, Object lock) {
+ public MagnificationController(AccessibilityManagerService ams, Object lock,
+ Context context) {
mAms = ams;
mLock = lock;
+ mContext = context;
+ }
+
+ @VisibleForTesting
+ public MagnificationController(AccessibilityManagerService ams, Object lock,
+ Context context, FullScreenMagnificationController fullScreenMagnificationController,
+ WindowMagnificationManager windowMagnificationManager) {
+ this(ams, lock, context);
+ mFullScreenMagnificationController = fullScreenMagnificationController;
+ mWindowMagnificationMgr = windowMagnificationManager;
}
/**
@@ -97,7 +114,7 @@ public class MagnificationTransitionController {
}
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
- final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationManager();
+ final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
final float scale = windowMagnificationMgr.getPersistedScale();
final DisableMagnificationCallback animationEndCallback =
new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
@@ -111,6 +128,39 @@ public class MagnificationTransitionController {
setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
}
+ /**
+ * Updates the active user ID of {@link FullScreenMagnificationController} and {@link
+ * WindowMagnificationManager}.
+ *
+ * @param userId the currently active user ID
+ */
+ public void updateUserIdIfNeeded(int userId) {
+ synchronized (mLock) {
+ if (mFullScreenMagnificationController != null) {
+ mFullScreenMagnificationController.setUserId(userId);
+ }
+ if (mWindowMagnificationMgr != null) {
+ mWindowMagnificationMgr.setUserId(userId);
+ }
+ }
+ }
+
+ /**
+ * Removes the magnification instance with given id.
+ *
+ * @param displayId The logical display id.
+ */
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ if (mFullScreenMagnificationController != null) {
+ mFullScreenMagnificationController.onDisplayRemoved(displayId);
+ }
+ if (mWindowMagnificationMgr != null) {
+ mWindowMagnificationMgr.onDisplayRemoved(displayId);
+ }
+ }
+ }
+
private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
int displayId) {
return mMagnificationEndRunnableSparseArray.get(displayId);
@@ -125,31 +175,63 @@ public class MagnificationTransitionController {
}
}
- private FullScreenMagnificationController getFullScreenMagnificationController() {
- return mAms.getFullScreenMagnificationController();
+ /**
+ * Getter of {@link FullScreenMagnificationController}.
+ *
+ * @return {@link FullScreenMagnificationController}.
+ */
+ public FullScreenMagnificationController getFullScreenMagnificationController() {
+ synchronized (mLock) {
+ if (mFullScreenMagnificationController == null) {
+ mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
+ mAms, mLock);
+ mFullScreenMagnificationController.setUserId(mAms.getCurrentUserIdLocked());
+ }
+ }
+ return mFullScreenMagnificationController;
+ }
+
+ /**
+ * Is {@link #mFullScreenMagnificationController} is initialized.
+ * @return {code true} if {@link #mFullScreenMagnificationController} is initialized.
+ */
+ public boolean isFullScreenMagnificationControllerInitialized() {
+ synchronized (mLock) {
+ return mFullScreenMagnificationController != null;
+ }
}
- private WindowMagnificationManager getWindowMagnificationManager() {
- return mAms.getWindowMagnificationMgr();
+ /**
+ * Getter of {@link WindowMagnificationManager}.
+ *
+ * @return {@link WindowMagnificationManager}.
+ */
+ public WindowMagnificationManager getWindowMagnificationMgr() {
+ synchronized (mLock) {
+ if (mWindowMagnificationMgr == null) {
+ mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
+ mAms.getCurrentUserIdLocked());
+ }
+ return mWindowMagnificationMgr;
+ }
}
private @Nullable
PointF getCurrentMagnificationBoundsCenterLocked(int displayId, int targetMode) {
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
- final WindowMagnificationManager magnificationManager = getWindowMagnificationManager();
- if (!magnificationManager.isWindowMagnifierEnabled(displayId)) {
+ if (mWindowMagnificationMgr == null
+ || !mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId)) {
return null;
}
- mTempPoint.set(magnificationManager.getCenterX(displayId),
- magnificationManager.getCenterY(displayId));
+ mTempPoint.set(mWindowMagnificationMgr.getCenterX(displayId),
+ mWindowMagnificationMgr.getCenterY(displayId));
} else {
- final FullScreenMagnificationController screenMagnificationController =
- getFullScreenMagnificationController();
- if (!screenMagnificationController.isMagnifying(displayId)) {
+ if (mFullScreenMagnificationController == null
+ || !mFullScreenMagnificationController.isMagnifying(displayId)) {
return null;
}
- mTempPoint.set(screenMagnificationController.getCenterX(displayId),
- screenMagnificationController.getCenterY(displayId));
+ mTempPoint.set(mFullScreenMagnificationController.getCenterX(displayId),
+ mFullScreenMagnificationController.getCenterY(displayId));
}
return mTempPoint;
}
@@ -228,7 +310,7 @@ public class MagnificationTransitionController {
mCurrentCenter.y, true,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
} else {
- getWindowMagnificationManager().enableWindowMagnification(mDisplayId,
+ getWindowMagnificationMgr().enableWindowMagnification(mDisplayId,
mCurrentScale, mCurrentCenter.x,
mCurrentCenter.y);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index c8e485f503ec..ed3085f97ded 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -452,6 +452,15 @@ public class WindowMagnificationManager implements
return magnifier;
}
+ /**
+ * Removes the window magnifier with given id.
+ *
+ * @param displayId The logical display id.
+ */
+ void onDisplayRemoved(int displayId) {
+ disableWindowMagnification(displayId, true);
+ }
+
private class ConnectionCallback extends IWindowMagnificationConnectionCallback.Stub implements
IBinder.DeathRecipient {
private boolean mExpiredDeathRecipient = false;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8a1baf25481b..da5d1c2aa61e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -136,8 +136,6 @@ import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
import android.net.shared.PrivateDnsConfig;
-import android.net.util.LinkPropertiesUtils.CompareOrUpdateResult;
-import android.net.util.LinkPropertiesUtils.CompareResult;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
import android.os.Binder;
@@ -195,6 +193,8 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.XmlUtils;
+import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
+import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.AutodestructReference;
import com.android.server.connectivity.DataConnectionStats;
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index 4a1820a8e538..d90750548114 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -25,7 +25,6 @@ import android.net.Uri;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
-import android.net.util.nsd.DnsSdTxtRecord;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
@@ -42,6 +41,7 @@ import com.android.internal.util.AsyncChannel;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.net.module.util.DnsSdTxtRecord;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 34e63705d781..b87468419430 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -17,13 +17,13 @@
package com.android.server;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.util.ArrayMap;
import android.util.Slog;
@@ -84,6 +84,13 @@ public final class SystemServiceManager {
@GuardedBy("mTargetUsers")
private final SparseArray<TargetUser> mTargetUsers = new SparseArray<>();
+ /**
+ * Reference to the current user, it's used to set the {@link TargetUser} on
+ * {@link #switchUser(int, int)} as the previous user might have been removed already.
+ */
+ @GuardedBy("mTargetUsers")
+ private @Nullable TargetUser mCurrentUser;
+
SystemServiceManager(Context context) {
mContext = context;
}
@@ -259,18 +266,22 @@ public final class SystemServiceManager {
return targetUser;
}
+ private @NonNull TargetUser newTargetUser(@UserIdInt int userId) {
+ final UserInfo userInfo = mUserManagerInternal.getUserInfo(userId);
+ Preconditions.checkState(userInfo != null, "No UserInfo for " + userId);
+ return new TargetUser(userInfo);
+ }
+
/**
* Starts the given user.
*/
public void startUser(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) {
- // Create cached TargetUser
- final UserInfo userInfo = mUserManagerInternal.getUserInfo(userId);
- Preconditions.checkState(userInfo != null, "No UserInfo for " + userId);
+ final TargetUser targetUser = newTargetUser(userId);
synchronized (mTargetUsers) {
- mTargetUsers.put(userId, new TargetUser(userInfo));
+ mTargetUsers.put(userId, targetUser);
}
- onUser(t, START, userId);
+ onUser(t, START, /* prevUser= */ null, targetUser);
}
/**
@@ -291,7 +302,26 @@ public final class SystemServiceManager {
* Switches to the given user.
*/
public void switchUser(@UserIdInt int from, @UserIdInt int to) {
- onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, to, from);
+ final TargetUser curUser, prevUser;
+ synchronized (mTargetUsers) {
+ if (mCurrentUser == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "First user switch: from " + from + " to " + to);
+ }
+ prevUser = newTargetUser(from);
+ } else {
+ if (from != mCurrentUser.getUserIdentifier()) {
+ Slog.wtf(TAG, "switchUser(" + from + "," + to + "): mCurrentUser is "
+ + mCurrentUser + ", it should be " + from);
+ }
+ prevUser = mCurrentUser;
+ }
+ curUser = mCurrentUser = getTargetUser(to);
+ if (DEBUG) {
+ Slog.d(TAG, "Set mCurrentUser to " + mCurrentUser);
+ }
+ }
+ onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, prevUser, curUser);
}
/**
@@ -314,21 +344,16 @@ public final class SystemServiceManager {
}
private void onUser(@NonNull String onWhat, @UserIdInt int userId) {
- onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userId);
+ onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, /* prevUser= */ null,
+ getTargetUser(userId));
}
private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
- @UserIdInt int userId) {
- onUser(t, onWhat, userId, UserHandle.USER_NULL);
- }
-
- private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
- @UserIdInt int curUserId, @UserIdInt int prevUserId) {
+ @Nullable TargetUser prevUser, @NonNull TargetUser curUser) {
+ final int curUserId = curUser.getUserIdentifier();
t.traceBegin("ssm." + onWhat + "User-" + curUserId);
- Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId);
- final TargetUser curUser = getTargetUser(curUserId);
- final TargetUser prevUser = prevUserId == UserHandle.USER_NULL ? null
- : getTargetUser(prevUserId);
+ Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId
+ + (prevUser != null ? " (from " + prevUser + ")" : ""));
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -466,14 +491,16 @@ public final class SystemServiceManager {
.append(service.getClass().getSimpleName())
.append("\n");
}
-
- builder.append("Target users: ");
- final int targetUsersSize = mTargetUsers.size();
- for (int i = 0; i < targetUsersSize; i++) {
- mTargetUsers.valueAt(i).dump(builder);
- if (i != targetUsersSize - 1) builder.append(',');
+ synchronized (mTargetUsers) {
+ builder.append("Current user: ").append(mCurrentUser).append('\n');
+ builder.append("Target users: ");
+ final int targetUsersSize = mTargetUsers.size();
+ for (int i = 0; i < targetUsersSize; i++) {
+ mTargetUsers.valueAt(i).dump(builder);
+ if (i != targetUsersSize - 1) builder.append(',');
+ }
+ builder.append('\n');
}
- builder.append('\n');
Slog.e(TAG, builder.toString());
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 65334fdee33c..ffdcd156122e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8253,7 +8253,7 @@ public class ActivityManagerService extends IActivityManager.Stub
private void dumpEverything(FileDescriptor fd, PrintWriter pw, String[] args, int opti,
boolean dumpAll, String dumpPackage, boolean dumpClient, boolean dumpNormalPriority,
- int dumpAppId) {
+ int dumpAppId, boolean dumpProxies) {
ActiveServices.ServiceDumper sdumper;
@@ -8312,7 +8312,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
sdumper.dumpWithClient();
}
- if (dumpPackage == null) {
+ if (dumpPackage == null && dumpProxies) {
// Intentionally dropping the lock for this, because dumpBinderProxies() will make many
// outgoing binder calls to retrieve interface descriptors; while that is system code,
// there is nothing preventing an app from overriding this implementation by talking to
@@ -8721,13 +8721,14 @@ public class ActivityManagerService extends IActivityManager.Stub
// dumpEverything() will take the lock when needed, and momentarily drop
// it for dumping client state.
dumpEverything(fd, pw, args, opti, dumpAll, dumpPackage, dumpClient,
- dumpNormalPriority, dumpAppId);
+ dumpNormalPriority, dumpAppId, true /* dumpProxies */);
} else {
// Take the lock here, so we get a consistent state for the entire dump;
- // dumpEverything() will take the lock as well, but that is fine.
+ // dumpEverything() will take the lock as well, which is fine for everything
+ // except dumping proxies, which can take a long time; exclude them.
synchronized(this) {
dumpEverything(fd, pw, args, opti, dumpAll, dumpPackage, dumpClient,
- dumpNormalPriority, dumpAppId);
+ dumpNormalPriority, dumpAppId, false /* dumpProxies */);
}
}
}
@@ -17333,4 +17334,18 @@ public class ActivityManagerService extends IActivityManager.Stub
throw new SecurityException("Caller uid " + callerUid + " cannot set freezer state ");
}
}
+
+ /**
+ * Holds the AM lock for the specified amount of milliseconds.
+ * Intended for use by the tests that need to imitate lock contention.
+ * Requires permission identity of the shell UID.
+ */
+ @Override
+ public void holdLock(int durationMs) {
+ enforceCallingPermission(Manifest.permission.INJECT_EVENTS, "holdLock");
+
+ synchronized (this) {
+ SystemClock.sleep(durationMs);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 1bf62a0c7b7e..58ac2dc869ce 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -733,10 +733,6 @@ public final class OomAdjuster {
uidRec.reset();
}
- if (mService.mAtmInternal != null) {
- mService.mAtmInternal.rankTaskLayersIfNeeded();
- }
-
mAdjSeq++;
if (fullUpdate) {
mNewNumServiceProcs = 0;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 2dced8d704bb..ebc5b59363df 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1909,8 +1909,8 @@ class ProcessRecord implements WindowProcessListener {
}
callback.initialize(this, adj, foregroundActivities, procState, schedGroup, appUid, logUid,
processCurTop);
- final int minLayer = getWindowProcessController().computeOomAdjFromActivities(
- ProcessList.VISIBLE_APP_LAYER_MAX, callback);
+ final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX,
+ getWindowProcessController().computeOomAdjFromActivities(callback));
mCachedAdj = callback.adj;
mCachedForegroundActivities = callback.foregroundActivities;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f1561cab8fb4..7ad0f21c2f69 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -297,6 +297,7 @@ public class AudioService extends IAudioService.Stub
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
private static final int MSG_DISABLE_AUDIO_FOR_UID = 100;
+ private static final int MSG_INIT_STREAMS_VOLUMES = 101;
// end of messages handled under wakelock
// retry delay in case of failure to indicate system ready to AudioFlinger
@@ -822,7 +823,34 @@ public class AudioService extends IAudioService.Stub
updateStreamVolumeAlias(false /*updateVolumes*/, TAG);
readPersistedSettings();
readUserRestrictions();
- mSettingsObserver = new SettingsObserver();
+
+ mPlaybackMonitor =
+ new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
+ mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
+
+ readAndSetLowRamDevice();
+
+ mIsCallScreeningModeSupported = AudioSystem.isCallScreeningModeSupported();
+
+ if (mSystemServer.isPrivileged()) {
+ LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
+
+ mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
+
+ mRecordMonitor.initMonitor();
+ }
+
+ mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
+
+ // done with service initialization, continue additional work in our Handler thread
+ queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_STREAMS_VOLUMES,
+ 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
+ }
+
+ /**
+ * Called by handling of MSG_INIT_STREAMS_VOLUMES
+ */
+ private void onInitStreamsAndVolumes() {
createStreamStates();
// must be called after createStreamStates() as it uses MUSIC volume as default if no
@@ -833,20 +861,42 @@ public class AudioService extends IAudioService.Stub
// relies on audio policy having correct ranges for volume indexes.
mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
- mPlaybackMonitor =
- new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
-
- mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
-
- readAndSetLowRamDevice();
-
- mIsCallScreeningModeSupported = AudioSystem.isCallScreeningModeSupported();
-
// Call setRingerModeInt() to apply correct mute
// state on streams affected by ringer mode.
mRingerAndZenModeMutedStreams = 0;
setRingerModeInt(getRingerModeInternal(), false);
+ final float[] preScale = new float[3];
+ preScale[0] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1,
+ 1, 1);
+ preScale[1] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index2,
+ 1, 1);
+ preScale[2] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index3,
+ 1, 1);
+ for (int i = 0; i < preScale.length; i++) {
+ if (0.0f <= preScale[i] && preScale[i] <= 1.0f) {
+ mPrescaleAbsoluteVolume[i] = preScale[i];
+ }
+ }
+
+ initExternalEventReceivers();
+
+ // check on volume initialization
+ checkVolumeRangeInitialization("AudioService()");
+ }
+
+ /**
+ * Initialize intent receives and settings observers for this service.
+ * Must be called after createStreamStates() as the handling of some events
+ * may affect or need volumes, e.g. BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED
+ * (for intent receiver), or Settings.Global.ZEN_MODE (for settings observer)
+ */
+ private void initExternalEventReceivers() {
+ mSettingsObserver = new SettingsObserver();
+
// Register for device connection intent broadcasts.
IntentFilter intentFilter =
new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
@@ -862,7 +912,6 @@ public class AudioService extends IAudioService.Stub
intentFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
if (mMonitorRotation) {
RotationHelper.init(mContext, mAudioHandler);
}
@@ -870,34 +919,8 @@ public class AudioService extends IAudioService.Stub
intentFilter.addAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
intentFilter.addAction(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
- context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
-
- if (mSystemServer.isPrivileged()) {
- LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
-
- mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
- mRecordMonitor.initMonitor();
- }
-
- final float[] preScale = new float[3];
- preScale[0] = mContext.getResources().getFraction(
- com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1,
- 1, 1);
- preScale[1] = mContext.getResources().getFraction(
- com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index2,
- 1, 1);
- preScale[2] = mContext.getResources().getFraction(
- com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index3,
- 1, 1);
- for (int i = 0; i < preScale.length; i++) {
- if (0.0f <= preScale[i] && preScale[i] <= 1.0f) {
- mPrescaleAbsoluteVolume[i] = preScale[i];
- }
- }
-
- // check on volume initialization
- checkVolumeRangeInitialization("AudioService()");
}
public void systemReady() {
@@ -2593,7 +2616,7 @@ public class AudioService extends IAudioService.Stub
// StreamVolumeCommand contains the information needed to defer the process of
// setStreamVolume() in case the user has to acknowledge the safe volume warning message.
- class StreamVolumeCommand {
+ static class StreamVolumeCommand {
public final int mStreamType;
public final int mIndex;
public final int mFlags;
@@ -2612,7 +2635,7 @@ public class AudioService extends IAudioService.Stub
.append(mIndex).append(",flags=").append(mFlags).append(",device=")
.append(mDevice).append('}').toString();
}
- };
+ }
private int getNewRingerMode(int stream, int index, int flags) {
// setRingerMode does nothing if the device is single volume,so the value would be unchanged
@@ -3321,7 +3344,7 @@ public class AudioService extends IAudioService.Stub
}
private int mRmtSbmxFullVolRefCount = 0;
- private ArrayList<RmtSbmxFullVolDeathHandler> mRmtSbmxFullVolDeathHandlers =
+ private final ArrayList<RmtSbmxFullVolDeathHandler> mRmtSbmxFullVolDeathHandlers =
new ArrayList<RmtSbmxFullVolDeathHandler>();
public void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb) {
@@ -5882,7 +5905,6 @@ public class AudioService extends IAudioService.Stub
private final Intent mStreamDevicesChanged;
private VolumeStreamState(String settingName, int streamType) {
-
mVolumeIndexSettingName = settingName;
mStreamType = streamType;
@@ -6658,6 +6680,11 @@ public class AudioService extends IAudioService.Stub
mAudioEventWakeLock.release();
break;
+ case MSG_INIT_STREAMS_VOLUMES:
+ onInitStreamsAndVolumes();
+ mAudioEventWakeLock.release();
+ break;
+
case MSG_CHECK_MUSIC_ACTIVE:
onCheckMusicActive((String) msg.obj);
break;
@@ -9216,7 +9243,7 @@ public class AudioService extends IAudioService.Stub
}
}
- private HashMap<IBinder, AsdProxy> mAudioServerStateListeners =
+ private final HashMap<IBinder, AsdProxy> mAudioServerStateListeners =
new HashMap<IBinder, AsdProxy>();
private void checkMonitorAudioServerStatePermission() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 2903b9970033..cc9407913be9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -46,6 +46,7 @@ import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.EventLog;
+import android.util.Pair;
import android.util.Slog;
import android.view.Surface;
@@ -79,7 +80,7 @@ public class FingerprintService extends SystemService {
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
private final LockPatternUtils mLockPatternUtils;
- private Fingerprint21 mFingerprint21;
+ @NonNull private List<ServiceProvider> mServiceProviders;
/**
* Receives the incoming binder calls from FingerprintManager.
@@ -88,11 +89,8 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public List<FingerprintSensorProperties> getSensorProperties(String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- final List<FingerprintSensorProperties> properties = new ArrayList<>();
-
- if (mFingerprint21 != null) {
- properties.add(mFingerprint21.getFingerprintSensorProperties());
- }
+ final List<FingerprintSensorProperties> properties =
+ FingerprintService.this.getSensorProperties();
Slog.d(TAG, "Retrieved sensor properties for: " + opPackageName
+ ", sensors: " + properties.size());
@@ -104,18 +102,26 @@ public class FingerprintService extends SystemService {
IFingerprintServiceReceiver receiver, String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- if (sensorId == mFingerprint21.getFingerprintSensorProperties().sensorId) {
- mFingerprint21.scheduleGenerateChallenge(token, receiver, opPackageName);
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId);
return;
}
- Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId);
+ provider.scheduleGenerateChallenge(sensorId, token, receiver, opPackageName);
}
@Override // Binder call
- public void revokeChallenge(IBinder token, String owner) {
+ public void revokeChallenge(IBinder token, String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- mFingerprint21.scheduleRevokeChallenge(token, owner);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for revokeChallenge");
+ return;
+ }
+
+ provider.second.scheduleRevokeChallenge(provider.first, token, opPackageName);
}
@Override // Binder call
@@ -123,14 +129,28 @@ public class FingerprintService extends SystemService {
final IFingerprintServiceReceiver receiver, final String opPackageName,
Surface surface) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- mFingerprint21.scheduleEnroll(token, hardwareAuthToken, userId, receiver, opPackageName,
- surface);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for enroll");
+ return;
+ }
+
+ provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
+ receiver, opPackageName, surface);
}
@Override // Binder call
public void cancelEnrollment(final IBinder token) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- mFingerprint21.cancelEnrollment(token);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for cancelEnrollment");
+ return;
+ }
+
+ provider.second.cancelEnrollment(provider.first, token);
}
@Override // Binder call
@@ -169,8 +189,15 @@ public class FingerprintService extends SystemService {
!= PackageManager.PERMISSION_GRANTED;
final int statsClient = isKeyguard ? BiometricsProtoEnums.CLIENT_KEYGUARD
: BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
- mFingerprint21.scheduleAuthenticate(token, operationId, userId, 0 /* cookie */,
- new ClientMonitorCallbackConverter(receiver), opPackageName,
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for authenticate");
+ return;
+ }
+
+ provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
+ 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
restricted, statsClient, isKeyguard);
}
@@ -191,7 +218,13 @@ public class FingerprintService extends SystemService {
return;
}
- mFingerprint21.scheduleFingerDetect(token, userId,
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for detectFingerprint");
+ return;
+ }
+
+ provider.second.scheduleFingerDetect(provider.first, token, userId,
new ClientMonitorCallbackConverter(receiver), opPackageName, surface,
BiometricsProtoEnums.CLIENT_KEYGUARD);
}
@@ -203,8 +236,14 @@ public class FingerprintService extends SystemService {
Surface surface) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for prepareForAuthentication");
+ return;
+ }
+
final boolean restricted = true; // BiometricPrompt is always restricted
- mFingerprint21.scheduleAuthenticate(token, operationId, userId, cookie,
+ provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, cookie,
new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, false /* isKeyguard */);
}
@@ -212,7 +251,14 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void startPreparedClient(int cookie) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
- mFingerprint21.startPreparedClient(cookie);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for startPreparedClient");
+ return;
+ }
+
+ provider.second.startPreparedClient(provider.first, cookie);
}
@@ -228,7 +274,13 @@ public class FingerprintService extends SystemService {
return;
}
- mFingerprint21.cancelAuthentication(token);
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for cancelAuthentication");
+ return;
+ }
+
+ provider.second.cancelAuthentication(provider.first, token);
}
@Override // Binder call
@@ -242,21 +294,41 @@ public class FingerprintService extends SystemService {
// For IBiometricsFingerprint2.1, cancelling fingerprint detect is the same as
// cancelling authentication.
- mFingerprint21.cancelAuthentication(token);
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for cancelFingerprintDetect");
+ return;
+ }
+
+ provider.second.cancelAuthentication(provider.first, token);
}
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
int callingUid, int callingPid, int callingUserId) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
- mFingerprint21.cancelAuthentication(token);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for cancelAuthenticationFromService");
+ return;
+ }
+
+ provider.second.cancelAuthentication(provider.first, token);
}
@Override // Binder call
public void remove(final IBinder token, final int fingerId, final int userId,
final IFingerprintServiceReceiver receiver, final String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- mFingerprint21.scheduleRemove(token, receiver, fingerId, userId, opPackageName);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for remove");
+ return;
+ }
+ provider.second.scheduleRemove(provider.first, token, receiver, fingerId, userId,
+ opPackageName);
}
@Override
@@ -274,10 +346,14 @@ public class FingerprintService extends SystemService {
final long ident = Binder.clearCallingIdentity();
try {
- if (args.length > 0 && "--proto".equals(args[0])) {
- mFingerprint21.dumpProto(fd);
- } else {
- mFingerprint21.dumpInternal(pw);
+ for (ServiceProvider provider : mServiceProviders) {
+ for (FingerprintSensorProperties props : provider.getSensorProperties()) {
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ provider.dumpProto(props.sensorId, fd);
+ } else {
+ provider.dumpInternal(props.sensorId, pw);
+ }
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -294,11 +370,12 @@ public class FingerprintService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
- if (mFingerprint21 == null) {
- Slog.e(TAG, "No HAL, caller: " + opPackageName);
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for isHardwareDetected, caller: " + opPackageName);
return false;
}
- return mFingerprint21.isHardwareDetected();
+ return provider.second.isHardwareDetected(provider.first);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -311,7 +388,13 @@ public class FingerprintService extends SystemService {
return;
}
- mFingerprint21.rename(fingerId, userId, name);
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for rename");
+ return;
+ }
+
+ provider.second.rename(provider.first, fingerId, userId, name);
}
@Override // Binder call
@@ -325,7 +408,8 @@ public class FingerprintService extends SystemService {
if (userId != UserHandle.getCallingUserId()) {
Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
}
- return mFingerprint21.getEnrolledFingerprints(userId);
+
+ return FingerprintService.this.getEnrolledFingerprints(userId, opPackageName);
}
@Override // Binder call
@@ -339,19 +423,32 @@ public class FingerprintService extends SystemService {
if (userId != UserHandle.getCallingUserId()) {
Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
}
- return mFingerprint21.getEnrolledFingerprints(userId).size() > 0;
+ return !FingerprintService.this.getEnrolledFingerprints(userId, opPackageName)
+ .isEmpty();
}
@Override // Binder call
public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- return mFingerprint21.getLockoutModeForUser(userId);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for getLockoutModeForUser");
+ return LockoutTracker.LOCKOUT_NONE;
+ }
+ return provider.second.getLockoutModeForUser(provider.first, userId);
}
@Override // Binder call
public long getAuthenticatorId(int userId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- return mFingerprint21.getAuthenticatorId(userId);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for getAuthenticatorId");
+ return 0;
+ }
+ return provider.second.getAuthenticatorId(provider.first, userId);
}
@Override // Binder call
@@ -359,12 +456,13 @@ public class FingerprintService extends SystemService {
@Nullable byte [] hardwareAuthToken, String opPackageName) {
Utils.checkPermission(getContext(), RESET_FINGERPRINT_LOCKOUT);
- if (sensorId == mFingerprint21.getFingerprintSensorProperties().sensorId) {
- mFingerprint21.scheduleResetLockout(userId);
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName);
return;
}
- Slog.w(TAG, "No matching sensor for resetLockout, sensorId: " + sensorId);
+ provider.second.scheduleResetLockout(sensorId, userId, hardwareAuthToken);
}
@Override
@@ -389,35 +487,52 @@ public class FingerprintService extends SystemService {
public void initializeConfiguration(int sensorId, int strength) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ final Fingerprint21 fingerprint21;
if ((Build.IS_USERDEBUG || Build.IS_ENG)
&& getContext().getResources().getBoolean(R.bool.allow_test_udfps)
&& Settings.Secure.getIntForUser(getContext().getContentResolver(),
Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
UserHandle.USER_CURRENT) != 0) {
- mFingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), sensorId,
+ fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), sensorId,
strength, mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
} else {
- mFingerprint21 = Fingerprint21.newInstance(getContext(), sensorId, strength,
+ fingerprint21 = Fingerprint21.newInstance(getContext(), sensorId, strength,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
+ mServiceProviders.add(fingerprint21);
}
@Override
- public void onFingerDown(int x, int y, float minor, float major) {
+ public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- mFingerprint21.onFingerDown(x, y, minor, major);
+
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG, "No matching provider for onFingerDown, sensorId: " + sensorId);
+ return;
+ }
+ provider.onFingerDown(sensorId, x, y, minor, major);
}
@Override
- public void onFingerUp() {
+ public void onFingerUp(int sensorId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- mFingerprint21.onFingerUp();
+
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG, "No matching provider for onFingerUp, sensorId: " + sensorId);
+ return;
+ }
+ provider.onFingerUp(sensorId);
}
@Override
- public void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+ public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- mFingerprint21.setUdfpsOverlayController(controller);
+
+ for (ServiceProvider provider : mServiceProviders) {
+ provider.setUdfpsOverlayController(controller);
+ }
}
}
@@ -427,6 +542,7 @@ public class FingerprintService extends SystemService {
mGestureAvailabilityDispatcher = new GestureAvailabilityDispatcher();
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
mLockPatternUtils = new LockPatternUtils(context);
+ mServiceProviders = new ArrayList<>();
}
@Override
@@ -434,6 +550,61 @@ public class FingerprintService extends SystemService {
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
}
+ @Nullable
+ private ServiceProvider getProviderForSensor(int sensorId) {
+ for (ServiceProvider provider : mServiceProviders) {
+ if (provider.containsSensor(sensorId)) {
+ return provider;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * For devices with only a single provider, returns that provider. If no providers, or multiple
+ * providers exist, returns null.
+ */
+ @Nullable
+ private Pair<Integer, ServiceProvider> getSingleProvider() {
+ final List<FingerprintSensorProperties> properties = getSensorProperties();
+ if (properties.size() != 1) {
+ return null;
+ }
+
+ // Theoretically we can just return the first provider, but maybe this is easier to
+ // understand.
+ final int sensorId = properties.get(0).sensorId;
+ for (ServiceProvider provider : mServiceProviders) {
+ if (provider.containsSensor(sensorId)) {
+ return new Pair<>(sensorId, provider);
+ }
+ }
+
+ Slog.e(TAG, "Single sensor, but provider not found");
+ return null;
+ }
+
+ @NonNull
+ private List<FingerprintSensorProperties> getSensorProperties() {
+ final List<FingerprintSensorProperties> properties = new ArrayList<>();
+
+ for (ServiceProvider provider : mServiceProviders) {
+ properties.addAll(provider.getSensorProperties());
+ }
+ return properties;
+ }
+
+ @NonNull
+ private List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for getEnrolledFingerprints, caller: " + opPackageName);
+ return Collections.emptyList();
+ }
+
+ return provider.second.getEnrolledFingerprints(provider.first, userId);
+ }
+
/**
* Checks for public API invocations to ensure that permissions, etc are granted/correct.
*/
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
new file mode 100644
index 000000000000..1fcc58cd5833
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.view.Surface;
+
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutTracker;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Superset of features/functionalities that HALs provide to the rest of the framework. This is
+ * more or less mapped to the public and private APIs that {@link FingerprintManager} provide, and
+ * is used at the system server layer to provide easy mapping between request and provider.
+ *
+ * Note that providers support both single-sensor and multi-sensor HALs. In either case,
+ * {@link FingerprintService} must ensure that providers are only requested to perform operations
+ * on sensors that they own.
+ *
+ * For methods other than {@link #containsSensor(int)}, the caller must ensure that the sensorId
+ * passed in is supported by the provider. For example,
+ * if (serviceProvider.containsSensor(sensorId)) {
+ * serviceProvider.operation(sensorId, ...);
+ * }
+ *
+ * For operations that are supported by some providers but not others, clients are required
+ * to check (e.g. via {@link FingerprintManager#getSensorProperties()}) to ensure that the code
+ * path isn't taken. ServiceProviders will provide a no-op for unsupported operations to
+ * fail safely.
+ */
+@SuppressWarnings("deprecation")
+public interface ServiceProvider {
+ /**
+ * Checks if the specified sensor is owned by this provider.
+ */
+ boolean containsSensor(int sensorId);
+
+ @NonNull List<FingerprintSensorProperties> getSensorProperties();
+
+ void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken);
+
+ void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, String opPackageName);
+
+ void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
+ @NonNull String opPackageName);
+
+ void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId,
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
+ @Nullable Surface surface);
+
+ void cancelEnrollment(int sensorId, @NonNull IBinder token);
+
+ void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
+ @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
+ @Nullable Surface surface, int statsClient);
+
+ void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
+ int cookie, @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull String opPackageName, boolean restricted, int statsClient, boolean isKeyguard);
+
+ void startPreparedClient(int sensorId, int cookie);
+
+ void cancelAuthentication(int sensorId, @NonNull IBinder token);
+
+ void scheduleRemove(int sensorId, @NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
+ @NonNull String opPackageName);
+
+ boolean isHardwareDetected(int sensorId);
+
+ void rename(int sensorId, int fingerId, int userId, @NonNull String name);
+
+ @NonNull List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId);
+
+ @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId);
+
+ long getAuthenticatorId(int sensorId, int userId);
+
+ void onFingerDown(int sensorId, int x, int y, float minor, float major);
+
+ void onFingerUp(int sensorId);
+
+ void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller);
+
+ void dumpProto(int sensorId, @NonNull FileDescriptor fd);
+
+ void dumpInternal(int sensorId, @NonNull PrintWriter pw);
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index c87bfec85dc3..30cbf40398ab 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -66,6 +66,7 @@ import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
import org.json.JSONArray;
import org.json.JSONException;
@@ -83,14 +84,14 @@ import java.util.Map;
* Supports a single instance of the {@link android.hardware.biometrics.fingerprint.V2_1} or
* its extended minor versions.
*/
-public class Fingerprint21 implements IHwBinder.DeathRecipient {
+public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider {
private static final String TAG = "Fingerprint21";
private static final int ENROLL_TIMEOUT_SEC = 60;
final Context mContext;
private final IActivityTaskManager mActivityTaskManager;
- private final FingerprintSensorProperties mSensorProperties;
+ @NonNull private final FingerprintSensorProperties mSensorProperties;
private final BiometricScheduler mScheduler;
private final Handler mHandler;
private final LockoutResetDispatcher mLockoutResetDispatcher;
@@ -435,9 +436,6 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
@Nullable IUdfpsOverlayController getUdfpsOverlayController() {
return mUdfpsOverlayController;
}
- @LockoutTracker.LockoutMode public int getLockoutModeForUser(int userId) {
- return mLockoutTracker.getLockoutModeForUser(userId);
- }
private void scheduleLoadAuthenticatorIds() {
// Note that this can be performed on the scheduler (as opposed to being done immediately
@@ -466,7 +464,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
* correct.
*/
private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
- final boolean hasEnrolled = !getEnrolledFingerprints(targetUserId).isEmpty();
+ final boolean hasEnrolled =
+ !getEnrolledFingerprints(mSensorProperties.sensorId, targetUserId).isEmpty();
final FingerprintUpdateActiveUserClient client =
new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
mContext.getOpPackageName(), mSensorProperties.sensorId, mCurrentUserId,
@@ -481,7 +480,21 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
});
}
- public void scheduleResetLockout(int userId) {
+ @Override
+ public boolean containsSensor(int sensorId) {
+ return mSensorProperties.sensorId == sensorId;
+ }
+
+ @Override
+ @NonNull
+ public List<FingerprintSensorProperties> getSensorProperties() {
+ final List<FingerprintSensorProperties> properties = new ArrayList<>();
+ properties.add(mSensorProperties);
+ return properties;
+ }
+
+ @Override
+ public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
// Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
// thread.
mHandler.post(() -> {
@@ -489,7 +502,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
});
}
- public void scheduleGenerateChallenge(@NonNull IBinder token,
+ @Override
+ public void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
final FingerprintGenerateChallengeClient client =
@@ -500,7 +514,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
});
}
- public void scheduleRevokeChallenge(@NonNull IBinder token, @NonNull String opPackageName) {
+ @Override
+ public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
+ @NonNull String opPackageName) {
mHandler.post(() -> {
final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
mContext, mLazyDaemon, token, opPackageName, mSensorProperties.sensorId);
@@ -508,7 +524,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
});
}
- public void scheduleEnroll(@NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId,
+ @Override
+ public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ @NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
@Nullable Surface surface) {
mHandler.post(() -> {
@@ -531,13 +549,15 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
});
}
- public void cancelEnrollment(@NonNull IBinder token) {
+ @Override
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
mHandler.post(() -> {
mScheduler.cancelEnrollment(token);
});
}
- public void scheduleFingerDetect(@NonNull IBinder token, int userId,
+ @Override
+ public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
@Nullable Surface surface, int statsClient) {
mHandler.post(() -> {
@@ -552,8 +572,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
});
}
- public void scheduleAuthenticate(@NonNull IBinder token, long operationId, int userId,
- int cookie, @NonNull ClientMonitorCallbackConverter listener,
+ @Override
+ public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+ int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
@NonNull String opPackageName, boolean restricted, int statsClient,
boolean isKeyguard) {
mHandler.post(() -> {
@@ -569,19 +590,22 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
});
}
- public void startPreparedClient(int cookie) {
+ @Override
+ public void startPreparedClient(int sensorId, int cookie) {
mHandler.post(() -> {
mScheduler.startPreparedClient(cookie);
});
}
- public void cancelAuthentication(@NonNull IBinder token) {
+ @Override
+ public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
mHandler.post(() -> {
mScheduler.cancelAuthentication(token);
});
}
- public void scheduleRemove(@NonNull IBinder token,
+ @Override
+ public void scheduleRemove(int sensorId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
@NonNull String opPackageName) {
mHandler.post(() -> {
@@ -599,7 +623,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
+ final List<Fingerprint> enrolledList = getEnrolledFingerprints(
+ mSensorProperties.sensorId, userId);
final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
mSensorProperties.sensorId, enrolledList, FingerprintUtils.getInstance(),
@@ -608,30 +633,37 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
});
}
- public boolean isHardwareDetected() {
+ @Override
+ public boolean isHardwareDetected(int sensorId) {
final IBiometricsFingerprint daemon = getDaemon();
return daemon != null;
}
- @NonNull public FingerprintSensorProperties getFingerprintSensorProperties() {
- return mSensorProperties;
- }
-
- public void rename(int fingerId, int userId, String name) {
+ @Override
+ public void rename(int sensorId, int fingerId, int userId, @NonNull String name) {
mHandler.post(() -> {
FingerprintUtils.getInstance().renameBiometricForUser(mContext, userId, fingerId, name);
});
}
- public List<Fingerprint> getEnrolledFingerprints(int userId) {
+ @Override
+ @NonNull
+ public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) {
return FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId);
}
- public long getAuthenticatorId(int userId) {
+ @Override
+ @LockoutTracker.LockoutMode public int getLockoutModeForUser(int sensorId, int userId) {
+ return mLockoutTracker.getLockoutModeForUser(userId);
+ }
+
+ @Override
+ public long getAuthenticatorId(int sensorId, int userId) {
return mAuthenticatorIds.get(userId);
}
- public void onFingerDown(int x, int y, float minor, float major) {
+ @Override
+ public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (!(client instanceof Udfps)) {
Slog.w(TAG, "onFingerDown received during client: " + client);
@@ -641,7 +673,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
udfps.onFingerDown(x, y, minor, major);
}
- public void onFingerUp() {
+ @Override
+ public void onFingerUp(int sensorId) {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (!(client instanceof Udfps)) {
Slog.w(TAG, "onFingerDown received during client: " + client);
@@ -651,11 +684,13 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
udfps.onFingerUp();
}
- public void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+ @Override
+ public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
mUdfpsOverlayController = controller;
}
- public void dumpProto(FileDescriptor fd) {
+ @Override
+ public void dumpProto(int sensorId, FileDescriptor fd) {
PerformanceTracker tracker =
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
@@ -695,7 +730,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient {
tracker.clear();
}
- public void dumpInternal(@NonNull PrintWriter pw) {
+ @Override
+ public void dumpInternal(int sensorId, @NonNull PrintWriter pw) {
PerformanceTracker performanceTracker =
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 6d8f241adbdc..d68671bcc679 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -46,6 +46,7 @@ import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import java.util.ArrayList;
+import java.util.List;
import java.util.Random;
/**
@@ -397,8 +398,9 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
// Schedule this only after we invoke onClientFinished for the previous client, so that
// internal preemption logic is not run.
- mFingerprint21.scheduleAuthenticate(token, operationId, user, cookie,
- listener, opPackageName, restricted, statsClient, isKeyguard);
+ mFingerprint21.scheduleAuthenticate(mFingerprint21.mSensorProperties.sensorId, token,
+ operationId, user, cookie, listener, opPackageName, restricted, statsClient,
+ isKeyguard);
}
}
@@ -451,12 +453,14 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
@Override
@NonNull
- public FingerprintSensorProperties getFingerprintSensorProperties() {
- return mSensorProperties;
+ public List<FingerprintSensorProperties> getSensorProperties() {
+ final List<FingerprintSensorProperties> properties = new ArrayList<>();
+ properties.add(mSensorProperties);
+ return properties;
}
@Override
- public void onFingerDown(int x, int y, float minor, float major) {
+ public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
mHandler.post(() -> {
Slog.d(TAG, "onFingerDown");
final AuthenticationConsumer lastAuthenticatedConsumer =
@@ -503,7 +507,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
}
@Override
- public void onFingerUp() {
+ public void onFingerUp(int sensorId) {
mHandler.post(() -> {
Slog.d(TAG, "onFingerUp");
@@ -558,7 +562,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
// Things can happen before SysUI loads and sets the controller.
if (controller != null) {
Slog.d(TAG, "setDebugMessage: " + message);
- controller.setDebugMessage(message);
+ controller.setDebugMessage(mSensorProperties.sensorId, message);
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when sending message: " + message, e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 8087e15b540d..0658f957fecb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -79,7 +79,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
if (authenticated) {
resetFailedAttempts(getTargetUserId());
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
mCallback.onClientFinished(this, true /* success */);
} else {
final @LockoutTracker.LockoutMode int lockoutMode =
@@ -92,7 +92,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
// Send the error, but do not invoke the FinishCallback yet. Since lockout is not
// controlled by the HAL, the framework must stop the sensor before finishing the
// client.
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
cancel();
}
@@ -111,7 +111,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
// GroupId was never used. In fact, groupId is always the same as userId.
getFreshDaemon().authenticate(mOperationId, getTargetUserId());
@@ -119,14 +119,14 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
getFreshDaemon().cancel();
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 5865617f4a44..cad2214891a7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -61,7 +61,7 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
getFreshDaemon().cancel();
} catch (RemoteException e) {
@@ -80,14 +80,14 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
getFreshDaemon().authenticate(0 /* operationId */, getTargetUserId());
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
mCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 1b9fae9cf91a..b1030bf367e8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -70,7 +70,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
// GroupId was never used. In fact, groupId is always the same as userId.
getFreshDaemon().enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec);
@@ -78,14 +78,14 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
getFreshDaemon().cancel();
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
index c71ecbf7577d..0f1d6b462cac 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
@@ -62,23 +62,25 @@ public class UdfpsHelper {
}
}
- static void showUdfpsOverlay(@Nullable IUdfpsOverlayController udfpsOverlayController) {
+ static void showUdfpsOverlay(int sensorId,
+ @Nullable IUdfpsOverlayController udfpsOverlayController) {
if (udfpsOverlayController == null) {
return;
}
try {
- udfpsOverlayController.showUdfpsOverlay();
+ udfpsOverlayController.showUdfpsOverlay(sensorId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when showing the UDFPS overlay", e);
}
}
- static void hideUdfpsOverlay(@Nullable IUdfpsOverlayController udfpsOverlayController) {
+ static void hideUdfpsOverlay(int sensorId,
+ @Nullable IUdfpsOverlayController udfpsOverlayController) {
if (udfpsOverlayController == null) {
return;
}
try {
- udfpsOverlayController.hideUdfpsOverlay();
+ udfpsOverlayController.hideUdfpsOverlay(sensorId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when hiding the UDFPS overlay", e);
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 5d2f51230c12..507600783aa4 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -836,7 +836,7 @@ public class ClipboardService extends SystemService {
return;
}
if (Settings.Global.getInt(getContext().getContentResolver(),
- "clipboard_access_toast_enabled", 0) == 0) {
+ "clipboard_access_toast_enabled", 1) == 0) {
return;
}
// Don't notify if the app accessing the clipboard is the same as the current owner.
diff --git a/services/core/java/com/android/server/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
index 3e619200d414..0304cdc47515 100644
--- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java
+++ b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
@@ -23,7 +23,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.net.ConnectivityManager;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -70,8 +69,6 @@ public class DataConnectionStats extends BroadcastReceiver {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
- filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
mContext.registerReceiver(this, filter, null /* broadcastPermission */, mListenerHandler);
}
@@ -81,10 +78,7 @@ public class DataConnectionStats extends BroadcastReceiver {
if (action.equals(Intent.ACTION_SIM_STATE_CHANGED)) {
updateSimState(intent);
notePhoneDataConnectionState();
- } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
- action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
- notePhoneDataConnectionState();
- }
+ }
}
private void notePhoneDataConnectionState() {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 99dc58e20209..14b34780bfc9 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -77,6 +77,7 @@ import android.net.ipsec.ike.ChildSessionParams;
import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -142,6 +143,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -2301,7 +2303,7 @@ public class Vpn {
void onChildTransformCreated(
@NonNull Network network, @NonNull IpSecTransform transform, int direction);
- void onSessionLost(@NonNull Network network);
+ void onSessionLost(@NonNull Network network, @Nullable Exception exception);
}
/**
@@ -2458,7 +2460,7 @@ public class Vpn {
networkAgent.sendLinkProperties(lp);
} catch (Exception e) {
Log.d(TAG, "Error in ChildOpened for network " + network, e);
- onSessionLost(network);
+ onSessionLost(network, e);
}
}
@@ -2488,7 +2490,7 @@ public class Vpn {
mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform);
} catch (IOException e) {
Log.d(TAG, "Transform application failed for network " + network, e);
- onSessionLost(network);
+ onSessionLost(network, e);
}
}
@@ -2546,11 +2548,20 @@ public class Vpn {
Log.d(TAG, "Ike Session started for network " + network);
} catch (Exception e) {
Log.i(TAG, "Setup failed for network " + network + ". Aborting", e);
- onSessionLost(network);
+ onSessionLost(network, e);
}
});
}
+ /** Marks the state as FAILED, and disconnects. */
+ private void markFailedAndDisconnect(Exception exception) {
+ synchronized (Vpn.this) {
+ updateState(DetailedState.FAILED, exception.getMessage());
+ }
+
+ disconnectVpnRunner();
+ }
+
/**
* Handles loss of a session
*
@@ -2560,7 +2571,7 @@ public class Vpn {
* <p>This method MUST always be called on the mExecutor thread in order to ensure
* consistency of the Ikev2VpnRunner fields.
*/
- public void onSessionLost(@NonNull Network network) {
+ public void onSessionLost(@NonNull Network network, @Nullable Exception exception) {
if (!isActiveNetwork(network)) {
Log.d(TAG, "onSessionLost() called for obsolete network " + network);
@@ -2572,6 +2583,27 @@ public class Vpn {
return;
}
+ if (exception instanceof IkeProtocolException) {
+ final IkeProtocolException ikeException = (IkeProtocolException) exception;
+
+ switch (ikeException.getErrorType()) {
+ case IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN: // Fallthrough
+ case IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD: // Fallthrough
+ case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED: // Fallthrough
+ case IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED: // Fallthrough
+ case IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED: // Fallthrough
+ case IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE:
+ // All the above failures are configuration errors, and are terminal
+ markFailedAndDisconnect(exception);
+ return;
+ // All other cases possibly recoverable.
+ }
+ } else if (exception instanceof IllegalArgumentException) {
+ // Failed to build IKE/ChildSessionParams; fatal profile configuration error
+ markFailedAndDisconnect(exception);
+ return;
+ }
+
mActiveNetwork = null;
// Close all obsolete state, but keep VPN alive incase a usable network comes up.
@@ -2621,12 +2653,18 @@ public class Vpn {
}
/**
- * Cleans up all Ikev2VpnRunner internal state
+ * Disconnects and shuts down this VPN.
+ *
+ * <p>This method resets all internal Ikev2VpnRunner state, but unless called via
+ * VpnRunner#exit(), this Ikev2VpnRunner will still be listed as the active VPN of record
+ * until the next VPN is started, or the Ikev2VpnRunner is explicitly exited. This is
+ * necessary to ensure that the detailed state is shown in the Settings VPN menus; if the
+ * active VPN is cleared, Settings VPNs will not show the resultant state or errors.
*
* <p>This method MUST always be called on the mExecutor thread in order to ensure
* consistency of the Ikev2VpnRunner fields.
*/
- private void shutdownVpnRunner() {
+ private void disconnectVpnRunner() {
mActiveNetwork = null;
mIsRunning = false;
@@ -2640,9 +2678,13 @@ public class Vpn {
@Override
public void exitVpnRunner() {
- mExecutor.execute(() -> {
- shutdownVpnRunner();
- });
+ try {
+ mExecutor.execute(() -> {
+ disconnectVpnRunner();
+ });
+ } catch (RejectedExecutionException ignored) {
+ // The Ikev2VpnRunner has already shut down.
+ }
}
}
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
index 103f659cc258..fa03e59f2f2e 100644
--- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -60,12 +60,12 @@ import android.net.ipsec.ike.IkeTrafficSelector;
import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
-import android.net.util.IpRange;
import android.system.OsConstants;
import android.util.Log;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.HexDump;
+import com.android.net.module.util.IpRange;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -270,13 +270,13 @@ public class VpnIkev2Utils {
@Override
public void onClosed() {
Log.d(mTag, "IkeClosed for network " + mNetwork);
- mCallback.onSessionLost(mNetwork); // Server requested session closure. Retry?
+ mCallback.onSessionLost(mNetwork, null); // Server requested session closure. Retry?
}
@Override
public void onClosedExceptionally(@NonNull IkeException exception) {
Log.d(mTag, "IkeClosedExceptionally for network " + mNetwork, exception);
- mCallback.onSessionLost(mNetwork);
+ mCallback.onSessionLost(mNetwork, exception);
}
@Override
@@ -306,13 +306,13 @@ public class VpnIkev2Utils {
@Override
public void onClosed() {
Log.d(mTag, "ChildClosed for network " + mNetwork);
- mCallback.onSessionLost(mNetwork);
+ mCallback.onSessionLost(mNetwork, null);
}
@Override
public void onClosedExceptionally(@NonNull IkeException exception) {
Log.d(mTag, "ChildClosedExceptionally for network " + mNetwork, exception);
- mCallback.onSessionLost(mNetwork);
+ mCallback.onSessionLost(mNetwork, exception);
}
@Override
@@ -349,7 +349,7 @@ public class VpnIkev2Utils {
@Override
public void onLost(@NonNull Network network) {
Log.d(mTag, "Tearing down; lost network: " + network);
- mCallback.onSessionLost(network);
+ mCallback.onSessionLost(network, null);
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
new file mode 100644
index 000000000000..f4f77db91500
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.annotation.NonNull;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.display.DisplayManagerService.SyncRoot;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Container for all the display devices present in the system. If an object wants to get events
+ * about all the DisplayDevices without needing to listen to all of the DisplayAdapters, they can
+ * listen and interact with the instance of this class.
+ * <p>
+ * The collection of {@link DisplayDevice}s and their usage is protected by the provided
+ * {@link DisplayManagerService.SyncRoot} lock object.
+ */
+class DisplayDeviceRepository implements DisplayAdapter.Listener {
+ private static final String TAG = "DisplayDeviceRepository";
+
+ public static final int DISPLAY_DEVICE_EVENT_ADDED = 1;
+ public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2;
+ public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3;
+
+ /**
+ * List of all currently connected display devices. Indexed by the displayId.
+ * TODO: multi-display - break the notion that this is indexed by displayId.
+ */
+ @GuardedBy("mSyncRoot")
+ private final List<DisplayDevice> mDisplayDevices = new ArrayList<>();
+
+ /** Listener for {link DisplayDevice} events. */
+ private final Listener mListener;
+
+ /** Global lock object from {@link DisplayManagerService}. */
+ private final SyncRoot mSyncRoot;
+
+ DisplayDeviceRepository(@NonNull SyncRoot syncRoot, @NonNull Listener listener) {
+ mSyncRoot = syncRoot;
+ mListener = listener;
+ }
+
+ @Override
+ public void onDisplayDeviceEvent(DisplayDevice device, int event) {
+ switch (event) {
+ case DISPLAY_DEVICE_EVENT_ADDED:
+ handleDisplayDeviceAdded(device);
+ break;
+
+ case DISPLAY_DEVICE_EVENT_CHANGED:
+ handleDisplayDeviceChanged(device);
+ break;
+
+ case DISPLAY_DEVICE_EVENT_REMOVED:
+ handleDisplayDeviceRemoved(device);
+ break;
+ }
+ }
+
+ @Override
+ public void onTraversalRequested() {
+ mListener.onTraversalRequested();
+ }
+
+ public boolean containsLocked(DisplayDevice d) {
+ return mDisplayDevices.contains(d);
+ }
+
+ public int sizeLocked() {
+ return mDisplayDevices.size();
+ }
+
+ public void forEachLocked(Consumer<DisplayDevice> consumer) {
+ final int count = mDisplayDevices.size();
+ for (int i = 0; i < count; i++) {
+ consumer.accept(mDisplayDevices.get(i));
+ }
+ }
+
+ private void handleDisplayDeviceAdded(DisplayDevice device) {
+ synchronized (mSyncRoot) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ if (mDisplayDevices.contains(device)) {
+ Slog.w(TAG, "Attempted to add already added display device: " + info);
+ return;
+ }
+ Slog.i(TAG, "Display device added: " + info);
+ device.mDebugLastLoggedDeviceInfo = info;
+
+ mDisplayDevices.add(device);
+ mListener.onDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
+ }
+ }
+
+ private void handleDisplayDeviceChanged(DisplayDevice device) {
+ synchronized (mSyncRoot) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ if (!mDisplayDevices.contains(device)) {
+ Slog.w(TAG, "Attempted to change non-existent display device: " + info);
+ return;
+ }
+ mListener.onDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
+ }
+ }
+
+ private void handleDisplayDeviceRemoved(DisplayDevice device) {
+ synchronized (mSyncRoot) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ if (!mDisplayDevices.remove(device)) {
+ Slog.w(TAG, "Attempted to remove non-existent display device: " + info);
+ return;
+ }
+
+ Slog.i(TAG, "Display device removed: " + info);
+ device.mDebugLastLoggedDeviceInfo = info;
+ mListener.onDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
+ }
+ }
+
+ /**
+ * Listens to {@link DisplayDevice} events from {@link DisplayDeviceRepository}.
+ */
+ interface Listener {
+ void onDisplayDeviceEventLocked(DisplayDevice device, int event);
+
+ // TODO: multi-display - Try to remove the need for requestTraversal...it feels like
+ // a shoe-horned method for a shoe-horned feature.
+ void onTraversalRequested();
+ };
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 97c4cf531a53..597f49c63193 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -131,7 +131,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
* </p><p>
* Display adapters are only weakly coupled to the display manager service.
* Display adapters communicate changes in display device state to the display manager
- * service asynchronously via a {@link DisplayAdapter.Listener} registered
+ * service asynchronously via a {@link DisplayAdapter.Listener}, and through
+ * the {@link DisplayDeviceRepository.Listener}, which is ultimately registered
* by the display manager service. This separation of concerns is important for
* two main reasons. First, it neatly encapsulates the responsibilities of these
* two classes: display adapters handle individual display devices whereas
@@ -180,7 +181,7 @@ public final class DisplayManagerService extends SystemService {
private final Context mContext;
private final DisplayManagerHandler mHandler;
private final Handler mUiHandler;
- private final DisplayAdapterListener mDisplayAdapterListener;
+ private final DisplayDeviceListener mDisplayDeviceListener;
private final DisplayModeDirector mDisplayModeDirector;
private WindowManagerInternal mWindowManagerInternal;
private InputManagerInternal mInputManagerInternal;
@@ -215,8 +216,7 @@ public final class DisplayManagerService extends SystemService {
// List of all currently registered display adapters.
private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
- // List of all currently connected display devices.
- private final ArrayList<DisplayDevice> mDisplayDevices = new ArrayList<DisplayDevice>();
+ private final DisplayDeviceRepository mDisplayDeviceRepo;
// List of all logical displays indexed by logical display id.
// Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
@@ -331,7 +331,8 @@ public final class DisplayManagerService extends SystemService {
mContext = context;
mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
mUiHandler = UiThread.getHandler();
- mDisplayAdapterListener = new DisplayAdapterListener();
+ mDisplayDeviceListener = new DisplayDeviceListener();
+ mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mDisplayDeviceListener);
mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
Resources resources = mContext.getResources();
@@ -469,6 +470,11 @@ public final class DisplayManagerService extends SystemService {
return mHandler;
}
+ @VisibleForTesting
+ DisplayDeviceRepository getDisplayDeviceRepository() {
+ return mDisplayDeviceRepo;
+ }
+
private void loadStableDisplayValuesLocked() {
final Point size = mPersistentDataStore.getStableDisplaySize();
if (size.x > 0 && size.y > 0) {
@@ -818,7 +824,17 @@ public final class DisplayManagerService extends SystemService {
return -1;
}
- handleDisplayDeviceAddedLocked(device);
+ // DisplayDevice events are handled manually for Virtual Displays.
+ // TODO: multi-display Fix this so that generic add/remove events are not handled in a
+ // different code path for virtual displays. Currently this happens so that we can
+ // return a valid display ID synchronously upon successful Virtual Display creation.
+ // This code can run on any binder thread, while onDisplayDeviceAdded() callbacks are
+ // called on the DisplayThread (which we don't want to wait for?).
+ // One option would be to actually wait here on the binder thread
+ // to be notified when the virtual display is created (or failed).
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device,
+ DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
+
LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
if (display != null) {
return display.getDisplayIdLocked();
@@ -828,7 +844,8 @@ public final class DisplayManagerService extends SystemService {
Slog.w(TAG, "Rejecting request to create virtual display "
+ "because the logical display was not created.");
mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder());
- handleDisplayDeviceRemovedLocked(device);
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device,
+ DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
}
return -1;
}
@@ -863,7 +880,9 @@ public final class DisplayManagerService extends SystemService {
DisplayDevice device =
mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
if (device != null) {
- handleDisplayDeviceRemovedLocked(device);
+ // TODO - handle virtual displays the same as other display adapters.
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device,
+ DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
}
}
}
@@ -883,7 +902,7 @@ public final class DisplayManagerService extends SystemService {
synchronized (mSyncRoot) {
// main display adapter
registerDisplayAdapterLocked(new LocalDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
+ 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
@@ -891,7 +910,7 @@ public final class DisplayManagerService extends SystemService {
// early apps like SetupWizard/Launcher. In particular, SUW is displayed using
// the virtual display inside VR before any VR-specific apps even run.
mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext,
- mHandler, mDisplayAdapterListener);
+ mHandler, mDisplayDeviceRepo);
if (mVirtualDisplayAdapter != null) {
registerDisplayAdapterLocked(mVirtualDisplayAdapter);
}
@@ -909,7 +928,7 @@ public final class DisplayManagerService extends SystemService {
private void registerOverlayDisplayAdapterLocked() {
registerDisplayAdapterLocked(new OverlayDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener, mUiHandler));
+ mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler));
}
private void registerWifiDisplayAdapterLocked() {
@@ -917,7 +936,7 @@ public final class DisplayManagerService extends SystemService {
com.android.internal.R.bool.config_enableWifiDisplay)
|| SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) {
mWifiDisplayAdapter = new WifiDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener,
+ mSyncRoot, mContext, mHandler, mDisplayDeviceRepo,
mPersistentDataStore);
registerDisplayAdapterLocked(mWifiDisplayAdapter);
}
@@ -946,15 +965,6 @@ public final class DisplayManagerService extends SystemService {
}
private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
- DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
- if (mDisplayDevices.contains(device)) {
- Slog.w(TAG, "Attempted to add already added display device: " + info);
- return;
- }
- Slog.i(TAG, "Display device added: " + info);
- device.mDebugLastLoggedDeviceInfo = info;
-
- mDisplayDevices.add(device);
LogicalDisplay display = addLogicalDisplayLocked(device);
Runnable work = updateDisplayStateLocked(device);
if (work != null) {
@@ -966,45 +976,45 @@ public final class DisplayManagerService extends SystemService {
@VisibleForTesting
void handleDisplayDeviceChanged(DisplayDevice device) {
synchronized (mSyncRoot) {
- DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
- if (!mDisplayDevices.contains(device)) {
- Slog.w(TAG, "Attempted to change non-existent display device: " + info);
- return;
- }
+ handleDisplayDeviceChangedLocked(device);
+ }
+ }
- int diff = device.mDebugLastLoggedDeviceInfo.diff(info);
- if (diff == DisplayDeviceInfo.DIFF_STATE) {
- Slog.i(TAG, "Display device changed state: \"" + info.name
- + "\", " + Display.stateToString(info.state));
- final Optional<Integer> viewportType = getViewportType(info);
- if (viewportType.isPresent()) {
- for (DisplayViewport d : mViewports) {
- if (d.type == viewportType.get() && info.uniqueId.equals(d.uniqueId)) {
- // Update display view port power state
- d.isActive = Display.isActiveState(info.state);
- }
- }
- if (mInputManagerInternal != null) {
- mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);
+ private void handleDisplayDeviceChangedLocked(DisplayDevice device) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+
+ int diff = device.mDebugLastLoggedDeviceInfo.diff(info);
+ if (diff == DisplayDeviceInfo.DIFF_STATE) {
+ Slog.i(TAG, "Display device changed state: \"" + info.name
+ + "\", " + Display.stateToString(info.state));
+ final Optional<Integer> viewportType = getViewportType(info);
+ if (viewportType.isPresent()) {
+ for (DisplayViewport d : mViewports) {
+ if (d.type == viewportType.get() && info.uniqueId.equals(d.uniqueId)) {
+ // Update display view port power state
+ d.isActive = Display.isActiveState(info.state);
}
}
- } else if (diff != 0) {
- Slog.i(TAG, "Display device changed: " + info);
- }
- if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) {
- try {
- mPersistentDataStore.setColorMode(device, info.colorMode);
- } finally {
- mPersistentDataStore.saveIfNeeded();
+ if (mInputManagerInternal != null) {
+ mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);
}
}
- device.mDebugLastLoggedDeviceInfo = info;
-
- device.applyPendingDisplayDeviceInfoChangesLocked();
- if (updateLogicalDisplaysLocked()) {
- scheduleTraversalLocked(false);
+ } else if (diff != 0) {
+ Slog.i(TAG, "Display device changed: " + info);
+ }
+ if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) {
+ try {
+ mPersistentDataStore.setColorMode(device, info.colorMode);
+ } finally {
+ mPersistentDataStore.saveIfNeeded();
}
}
+ device.mDebugLastLoggedDeviceInfo = info;
+
+ device.applyPendingDisplayDeviceInfoChangesLocked();
+ if (updateLogicalDisplaysLocked()) {
+ scheduleTraversalLocked(false);
+ }
}
private void handleDisplayDeviceRemoved(DisplayDevice device) {
@@ -1014,15 +1024,6 @@ public final class DisplayManagerService extends SystemService {
}
private void handleDisplayDeviceRemovedLocked(DisplayDevice device) {
- DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
- if (!mDisplayDevices.remove(device)) {
- Slog.w(TAG, "Attempted to remove non-existent display device: " + info);
- return;
- }
-
- Slog.i(TAG, "Display device removed: " + info);
- device.mDebugLastLoggedDeviceInfo = info;
-
updateLogicalDisplaysLocked();
scheduleTraversalLocked(false);
}
@@ -1043,14 +1044,12 @@ public final class DisplayManagerService extends SystemService {
}
private void applyGlobalDisplayStateLocked(List<Runnable> workQueue) {
- final int count = mDisplayDevices.size();
- for (int i = 0; i < count; i++) {
- DisplayDevice device = mDisplayDevices.get(i);
+ mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
Runnable runnable = updateDisplayStateLocked(device);
if (runnable != null) {
workQueue.add(runnable);
}
- }
+ });
}
private Runnable updateDisplayStateLocked(DisplayDevice device) {
@@ -1058,6 +1057,8 @@ public final class DisplayManagerService extends SystemService {
// by the display power controller (if known).
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
+ // TODO - multi-display - The rules regarding what display state to apply to each
+ // display will depend on the configuration/mapping of logical displays.
return device.requestDisplayStateLocked(
mGlobalDisplayState, mGlobalDisplayBrightness);
}
@@ -1085,7 +1086,7 @@ public final class DisplayManagerService extends SystemService {
final int layerStack = assignLayerStackLocked(displayId);
LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
- display.updateLocked(mDisplayDevices);
+ display.updateLocked(mDisplayDeviceRepo);
if (!display.isValidLocked()) {
// This should never happen currently.
Slog.w(TAG, "Ignoring display device because the logical display "
@@ -1248,7 +1249,7 @@ public final class DisplayManagerService extends SystemService {
mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
- display.updateLocked(mDisplayDevices);
+ display.updateLocked(mDisplayDeviceRepo);
if (!display.isValidLocked()) {
mLogicalDisplays.removeAt(i);
handleLogicalDisplayRemoved(displayId);
@@ -1276,12 +1277,10 @@ public final class DisplayManagerService extends SystemService {
clearViewportsLocked();
// Configure each display device.
- final int count = mDisplayDevices.size();
- for (int i = 0; i < count; i++) {
- DisplayDevice device = mDisplayDevices.get(i);
+ mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
configureDisplayLocked(t, device);
device.performTraversalLocked(t);
- }
+ });
// Tell the input system about these new viewports.
if (mInputManagerInternal != null) {
@@ -1714,11 +1713,11 @@ public final class DisplayManagerService extends SystemService {
}
pw.println();
- pw.println("Display Devices: size=" + mDisplayDevices.size());
- for (DisplayDevice device : mDisplayDevices) {
+ pw.println("Display Devices: size=" + mDisplayDeviceRepo.sizeLocked());
+ mDisplayDeviceRepo.forEachLocked(device -> {
pw.println(" " + device.getDisplayDeviceInfoLocked());
device.dumpLocked(ipw);
- }
+ });
final int logicalDisplayCount = mLogicalDisplays.size();
pw.println();
@@ -1864,20 +1863,20 @@ public final class DisplayManagerService extends SystemService {
}
}
- private final class DisplayAdapterListener implements DisplayAdapter.Listener {
+ private final class DisplayDeviceListener implements DisplayDeviceRepository.Listener {
@Override
- public void onDisplayDeviceEvent(DisplayDevice device, int event) {
+ public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {
switch (event) {
- case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED:
- handleDisplayDeviceAdded(device);
+ case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_ADDED:
+ handleDisplayDeviceAddedLocked(device);
break;
- case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED:
- handleDisplayDeviceChanged(device);
+ case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_CHANGED:
+ handleDisplayDeviceChangedLocked(device);
break;
- case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED:
- handleDisplayDeviceRemoved(device);
+ case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_REMOVED:
+ handleDisplayDeviceRemovedLocked(device);
break;
}
}
@@ -2573,9 +2572,10 @@ public final class DisplayManagerService extends SystemService {
}
}
};
+ LogicalDisplay defaultDisplay = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
+ DisplayDevice defaultDevice = defaultDisplay.getPrimaryDisplayDeviceLocked();
mDisplayPowerController = new DisplayPowerController(
- mContext, callbacks, handler, sensorManager, blanker,
- mDisplayDevices.get(Display.DEFAULT_DISPLAY));
+ mContext, callbacks, handler, sensorManager, blanker, defaultDevice);
mSensorManager = sensorManager;
}
@@ -2689,9 +2689,7 @@ public final class DisplayManagerService extends SystemService {
@Override
public void onOverlayChanged() {
synchronized (mSyncRoot) {
- for (int i = 0; i < mDisplayDevices.size(); i++) {
- mDisplayDevices.get(i).onOverlayChangedLocked();
- }
+ mDisplayDeviceRepo.forEachLocked(DisplayDevice::onOverlayChangedLocked);
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 8556f084a072..bf8b891cffb8 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -28,7 +28,6 @@ import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
import java.util.Arrays;
-import java.util.List;
import java.util.Objects;
/**
@@ -220,16 +219,16 @@ final class LogicalDisplay {
* The logical display might become invalid if it is attached to a display device
* that no longer exists.
*
- * @param devices The list of all connected display devices.
+ * @param deviceRepo Repository of active {@link DisplayDevice}s.
*/
- public void updateLocked(List<DisplayDevice> devices) {
+ public void updateLocked(DisplayDeviceRepository deviceRepo) {
// Nothing to update if already invalid.
if (mPrimaryDisplayDevice == null) {
return;
}
// Check whether logical display has become invalid.
- if (!devices.contains(mPrimaryDisplayDevice)) {
+ if (!deviceRepo.containsLocked(mPrimaryDisplayDevice)) {
mPrimaryDisplayDevice = null;
return;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index f2f6dbe9bde5..6e6d848c2acc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -58,11 +58,6 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
// If true, turn off TV upon standby. False by default.
private boolean mAutoTvOff;
- // Local active port number used for Routing Control.
- // Default 0 means HOME is the current active path. Temp solution only.
- // TODO(amyjojo): adding system constants for input ports to TIF mapping.
- private int mLocalActivePath = 0;
-
// Determines what action should be taken upon receiving Routing Control messages.
@VisibleForTesting
protected HdmiProperties.playback_device_action_on_routing_control_values
@@ -438,16 +433,6 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
checkIfPendingActionsCleared();
}
- private void routeToPort(int portId) {
- // TODO(AMYJOJO): route to specific input of the port
- mLocalActivePath = portId;
- }
-
- @VisibleForTesting
- protected int getLocalActivePath() {
- return mLocalActivePath;
- }
-
@Override
protected void dump(final IndentingPrintWriter pw) {
super.dump(pw);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 7c98c6c7947b..cc8a330b5edb 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -46,6 +46,7 @@ import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
+import android.hardware.input.InputManagerInternal.LidSwitchCallback;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
import android.media.AudioManager;
@@ -189,6 +190,10 @@ public class InputManagerService extends IInputManager.Stub
private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>();
private int mNextVibratorTokenValue;
+ // State for lid switch
+ private final Object mLidSwitchLock = new Object();
+ private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>();
+
// State for the currently installed input filter.
final Object mInputFilterLock = new Object();
IInputFilter mInputFilter; // guarded by mInputFilterLock
@@ -233,7 +238,7 @@ public class InputManagerService extends IInputManager.Stub
private static native void nativeToggleCapsLock(long ptr, int deviceId);
private static native void nativeDisplayRemoved(long ptr, int displayId);
private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
- private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
+ private static native void nativeSetSystemUiLightsOut(long ptr, boolean lightsOut);
private static native void nativeSetFocusedApplication(long ptr,
int displayId, InputApplicationHandle application);
private static native void nativeSetFocusedDisplay(long ptr, int displayId);
@@ -330,6 +335,9 @@ public class InputManagerService extends IInputManager.Stub
public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
+ /** Indicates an open state for the lid switch. */
+ public static final int SW_STATE_LID_OPEN = 0;
+
/** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
final boolean mUseDevInputEventForAudioJack;
@@ -353,13 +361,33 @@ public class InputManagerService extends IInputManager.Stub
}
public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
+ if (mWindowManagerCallbacks != null) {
+ unregisterLidSwitchCallbackInternal(mWindowManagerCallbacks);
+ }
mWindowManagerCallbacks = callbacks;
+ registerLidSwitchCallbackInternal(mWindowManagerCallbacks);
}
public void setWiredAccessoryCallbacks(WiredAccessoryCallbacks callbacks) {
mWiredAccessoryCallbacks = callbacks;
}
+ void registerLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
+ boolean lidOpen;
+ synchronized (mLidSwitchLock) {
+ mLidSwitchCallbacks.add(callback);
+ lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
+ == SW_STATE_LID_OPEN;
+ }
+ callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
+ }
+
+ void unregisterLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
+ synchronized (mLidSwitchLock) {
+ mLidSwitchCallbacks.remove(callback);
+ }
+ }
+
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
@@ -1560,8 +1588,8 @@ public class InputManagerService extends IInputManager.Stub
nativeSetInputDispatchMode(mPtr, enabled, frozen);
}
- public void setSystemUiVisibility(int visibility) {
- nativeSetSystemUiVisibility(mPtr, visibility);
+ public void setSystemUiLightsOut(boolean lightsOut) {
+ nativeSetSystemUiLightsOut(mPtr, lightsOut);
}
/**
@@ -1934,6 +1962,7 @@ public class InputManagerService extends IInputManager.Stub
synchronized (mInputFilterLock) { }
synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
synchronized (mGestureMonitorPidsLock) { /* Test if blocked by gesture monitor pids lock */}
+ synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }
nativeMonitor(mPtr);
}
@@ -1964,7 +1993,15 @@ public class InputManagerService extends IInputManager.Stub
if ((switchMask & SW_LID_BIT) != 0) {
final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
- mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
+
+ ArrayList<LidSwitchCallback> callbacksCopy;
+ synchronized (mLidSwitchLock) {
+ callbacksCopy = new ArrayList<>(mLidSwitchCallbacks);
+ }
+ for (int i = 0; i < callbacksCopy.size(); i++) {
+ LidSwitchCallback callbacks = callbacksCopy.get(i);
+ callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
+ }
}
if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
@@ -2263,20 +2300,13 @@ public class InputManagerService extends IInputManager.Stub
/**
* Callback interface implemented by the Window Manager.
*/
- public interface WindowManagerCallbacks {
+ public interface WindowManagerCallbacks extends LidSwitchCallback {
/**
* This callback is invoked when the confuguration changes.
*/
public void notifyConfigurationChanged();
/**
- * This callback is invoked when the lid switch changes state.
- * @param whenNanos the time when the change occurred
- * @param lidOpen true if the lid is open
- */
- public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
-
- /**
* This callback is invoked when the camera lens cover switch changes state.
* @param whenNanos the time when the change occurred
* @param lensCovered true is the lens is covered
@@ -2603,6 +2633,16 @@ public class InputManagerService extends IInputManager.Stub
@NonNull IBinder toChannelToken) {
return InputManagerService.this.transferTouchFocus(fromChannelToken, toChannelToken);
}
+
+ @Override
+ public void registerLidSwitchCallback(LidSwitchCallback callbacks) {
+ registerLidSwitchCallbackInternal(callbacks);
+ }
+
+ @Override
+ public void unregisterLidSwitchCallback(LidSwitchCallback callbacks) {
+ unregisterLidSwitchCallbackInternal(callbacks);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index bba248c2ab74..2eccaf1434b4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -17,6 +17,9 @@ package com.android.server.inputmethod;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.CLIENTS;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ELAPSED_REALTIME_NANOS;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.ENTRY;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -96,12 +99,15 @@ import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.Log;
import android.util.LruCache;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.ContextThemeWrapper;
import android.view.DisplayInfo;
import android.view.IWindowManager;
@@ -1702,7 +1708,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Intent intent = new Intent(ACTION_SHOW_INPUT_METHOD_PICKER)
.setPackage(mContext.getPackageName());
- mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_IMMUTABLE);
mShowOngoingImeSwitcherForPhones = false;
@@ -2530,7 +2537,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.input_method_binding_label);
mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
- mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
+ mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
+ PendingIntent.FLAG_IMMUTABLE));
if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) {
mLastBindTime = SystemClock.uptimeMillis();
@@ -3463,6 +3471,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final boolean sameWindowFocused = mCurFocusedWindow == windowToken;
final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0;
+ final boolean startInputByWinGainedFocus =
+ (startInputFlags & StartInputFlags.WINDOW_GAINED_FOCUS) != 0;
+
if (sameWindowFocused && isTextEditor) {
if (DEBUG) {
Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
@@ -3506,7 +3517,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
InputBindResult res = null;
switch (softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE) {
case LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
- if (!isTextEditor || !doAutoShow) {
+ if (!sameWindowFocused && (!isTextEditor || !doAutoShow)) {
if (LayoutParams.mayUseInputMethod(windowFlags)) {
// There is no focus view, and this window will
// be behind any soft input window, so hide the
@@ -3555,7 +3566,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
break;
case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
- if (isImeVisible()) {
+ if (!sameWindowFocused) {
if (DEBUG) Slog.v(TAG, "Window asks to hide input");
hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
@@ -3584,7 +3595,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (DEBUG) Slog.v(TAG, "Window asks to always show input");
if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
unverifiedTargetSdkVersion, startInputFlags)) {
- if (!isImeVisible()) {
+ if (!sameWindowFocused) {
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext, missingMethods,
attribute, startInputFlags, startInputReason);
@@ -3603,17 +3614,26 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (!didStart) {
if (attribute != null) {
- if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
+ if (sameWindowFocused) {
+ // On previous platforms, when Dialogs re-gained focus, the Activity behind
+ // would briefly gain focus first, and dismiss the IME.
+ // On R that behavior has been fixed, but unfortunately apps have come
+ // to rely on this behavior to hide the IME when the editor no longer has focus
+ // To maintain compatibility, we are now hiding the IME when we don't have
+ // an editor upon refocusing a window.
+ if (startInputByWinGainedFocus) {
+ hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR);
+ }
+ res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
+ startInputFlags, startInputReason);
+ } else if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
|| (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
startInputFlags, startInputReason);
} else {
res = InputBindResult.NO_EDITOR;
}
- } else if (sameWindowFocused) {
- return new InputBindResult(
- InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
- null, null, null, -1, null);
} else {
res = InputBindResult.NULL_EDITOR_INFO;
}
@@ -4018,6 +4038,55 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget();
}
+ /**
+ * Starting point for dumping the IME tracing information in proto format.
+ *
+ * @param clientProtoDump dump information from the IME client side
+ */
+ @BinderThread
+ @Override
+ public void startProtoDump(byte[] clientProtoDump) {
+ if (!ImeTracing.getInstance().isAvailable() || !ImeTracing.getInstance().isEnabled()) {
+ return;
+ }
+ if (clientProtoDump == null && mCurClient == null) {
+ return;
+ }
+
+ ProtoOutputStream proto = new ProtoOutputStream();
+ final long token = proto.start(ENTRY);
+ proto.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+ // TODO: get server side dump
+ if (clientProtoDump != null) {
+ proto.write(CLIENTS, clientProtoDump);
+ } else {
+ IBinder client = null;
+
+ synchronized (mMethodMap) {
+ if (mCurClient != null && mCurClient.client != null) {
+ client = mCurClient.client.asBinder();
+ }
+ }
+
+ if (client != null) {
+ try {
+ proto.write(CLIENTS,
+ TransferPipe.dumpAsync(client, ImeTracing.PROTO_ARG));
+ } catch (IOException | RemoteException e) {
+ Log.e(TAG, "Exception while collecting client side ime dump", e);
+ }
+ }
+ }
+ proto.end(token);
+ ImeTracing.getInstance().addToBuffer(proto);
+ }
+
+ @BinderThread
+ @Override
+ public boolean isImeTraceEnabled() {
+ return ImeTracing.getInstance().isEnabled();
+ }
+
@BinderThread
private void notifyUserAction(@NonNull IBinder token) {
if (DEBUG) {
@@ -5412,6 +5481,21 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return mService.handleShellCommandSetInputMethod(this);
case "reset":
return mService.handleShellCommandResetInputMethod(this);
+ case "tracing":
+ int result = ImeTracing.getInstance().onShellCommand(this);
+ boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled();
+ for (ClientState state : mService.mClients.values()) {
+ if (state != null) {
+ try {
+ state.client.setImeTraceEnabled(isImeTraceEnabled);
+ } catch (RemoteException e) {
+ Log.e(TAG,
+ "Error while trying to enable/disable ime "
+ + "trace on client window", e);
+ }
+ }
+ }
+ return result;
default:
getOutPrintWriter().println("Unknown command: " + imeCommand);
return ShellCommandResult.FAILURE;
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index b518eb1ab6d0..a6ca25b0e6c1 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1805,5 +1805,16 @@ public final class MultiClientInputMethodManagerService {
mUserDataMap.dump(fd, ipw, args);
}
}
+
+ @BinderThread
+ @Override
+ public void startProtoDump(byte[] clientProtoDump) throws RemoteException {
+ }
+
+ @BinderThread
+ @Override
+ public boolean isImeTraceEnabled() throws RemoteException {
+ return false;
+ }
}
}
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
index 25ef9facb216..c09ade9e075f 100644
--- a/services/core/java/com/android/server/inputmethod/OWNERS
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -4,3 +4,4 @@ ogunwale@google.com
yukawa@google.com
tarandeep@google.com
lumark@google.com
+roosa@google.com
diff --git a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
new file mode 100644
index 000000000000..38018779aed1
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.location.timezone.ILocationTimeZoneProvider;
+import com.android.internal.location.timezone.ILocationTimeZoneProviderManager;
+import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
+import com.android.server.ServiceWatcher;
+
+import java.util.Objects;
+
+/**
+ * System server-side proxy for ILocationTimeZoneProvider implementations, i.e. this provides the
+ * system server object used to communicate with a remote LocationTimeZoneProvider over Binder,
+ * which could be running in a different process. As "remote" LocationTimeZoneProviders are bound /
+ * unbound this proxy will rebind to the "best" available remote process.
+ */
+class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
+
+ /**
+ * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+ * null.
+ */
+ @Nullable
+ static LocationTimeZoneProviderProxy createAndRegister(
+ @NonNull Context context, @NonNull ThreadingDomain threadingDomain,
+ @NonNull String action, int enableOverlayResId, int nonOverlayPackageResId) {
+ RealLocationTimeZoneProviderProxy proxy = new RealLocationTimeZoneProviderProxy(
+ context, threadingDomain, action, enableOverlayResId, nonOverlayPackageResId);
+ if (proxy.register()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ @NonNull private final ServiceWatcher mServiceWatcher;
+
+ @GuardedBy("mProxyLock")
+ @Nullable private ManagerProxy mManagerProxy;
+
+ @GuardedBy("mProxyLock")
+ @NonNull private LocationTimeZoneProviderRequest mRequest;
+
+ private RealLocationTimeZoneProviderProxy(
+ @NonNull Context context, @NonNull ThreadingDomain threadingDomain,
+ @NonNull String action, int enableOverlayResId,
+ int nonOverlayPackageResId) {
+ super(context, threadingDomain);
+ mManagerProxy = null;
+ mRequest = LocationTimeZoneProviderRequest.EMPTY_REQUEST;
+ mServiceWatcher = new ServiceWatcher(context, action, this::onBind, this::onUnbind,
+ enableOverlayResId, nonOverlayPackageResId);
+ }
+
+ private boolean register() {
+ return mServiceWatcher.register();
+ }
+
+ private void onBind(IBinder binder, ComponentName componentName) throws RemoteException {
+ processServiceWatcherCallbackOnThreadingDomainThread(() -> onBindOnHandlerThread(binder));
+ }
+
+ private void onUnbind() {
+ processServiceWatcherCallbackOnThreadingDomainThread(this::onUnbindOnHandlerThread);
+ }
+
+ private void processServiceWatcherCallbackOnThreadingDomainThread(@NonNull Runnable runnable) {
+ // For simplicity, this code just post()s the runnable to the mThreadingDomain Thread in all
+ // cases. This adds a delay if ServiceWatcher and ThreadingDomain happen to be using the
+ // same thread, but nothing here should be performance critical.
+ mThreadingDomain.post(runnable);
+ }
+
+ private void onBindOnHandlerThread(@NonNull IBinder binder) {
+ mThreadingDomain.assertCurrentThread();
+
+ ILocationTimeZoneProvider provider = ILocationTimeZoneProvider.Stub.asInterface(binder);
+
+ synchronized (mSharedLock) {
+ try {
+ mManagerProxy = new ManagerProxy();
+ provider.setLocationTimeZoneProviderManager(mManagerProxy);
+ trySendCurrentRequest();
+ mListener.onProviderBound();
+ } catch (RemoteException e) {
+ // This is not expected to happen.
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private void onUnbindOnHandlerThread() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ mManagerProxy = null;
+ mListener.onProviderUnbound();
+ }
+ }
+
+ @Override
+ final void setRequest(@NonNull LocationTimeZoneProviderRequest request) {
+ mThreadingDomain.assertCurrentThread();
+
+ Objects.requireNonNull(request);
+ synchronized (mSharedLock) {
+ mRequest = request;
+
+ trySendCurrentRequest();
+ }
+ }
+
+ @GuardedBy("mProxyLock")
+ private void trySendCurrentRequest() {
+ LocationTimeZoneProviderRequest request = mRequest;
+ mServiceWatcher.runOnBinder(binder -> {
+ ILocationTimeZoneProvider service =
+ ILocationTimeZoneProvider.Stub.asInterface(binder);
+ service.setRequest(request);
+ });
+ }
+
+ @Override
+ public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+ synchronized (mSharedLock) {
+ ipw.println("mRequest=" + mRequest);
+ mServiceWatcher.dump(null, ipw, args);
+ }
+ }
+
+ /**
+ * A system Server-side proxy for the ILocationTimeZoneProviderManager, i.e. this is a local
+ * binder stub. Each "remote" LocationTimeZoneProvider is passed a binder instance that it
+ * then uses to communicate back with the system server, invoking the logic here.
+ */
+ private class ManagerProxy extends ILocationTimeZoneProviderManager.Stub {
+
+ // executed on binder thread
+ @Override
+ public void onLocationTimeZoneEvent(LocationTimeZoneEvent locationTimeZoneEvent) {
+ synchronized (mSharedLock) {
+ if (mManagerProxy != this) {
+ return;
+ }
+ }
+ handleLocationTimeZoneEvent(locationTimeZoneEvent);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaResourceMonitorService.java b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
index e5da9b1c178c..fc7dd30eec49 100644
--- a/services/core/java/com/android/server/media/MediaResourceMonitorService.java
+++ b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
@@ -28,6 +28,8 @@ import android.util.Log;
import com.android.server.SystemService;
+import java.util.List;
+
/** This class provides a system service that monitors media resource usage. */
public class MediaResourceMonitorService extends SystemService {
private static final String TAG = "MediaResourceMonitor";
@@ -60,16 +62,18 @@ public class MediaResourceMonitorService extends SystemService {
if (pkgNames == null) {
return;
}
- UserManager manager = getContext().getSystemService(UserManager.class);
- int[] userIds = manager.getEnabledProfileIds(ActivityManager.getCurrentUser());
- if (userIds == null || userIds.length == 0) {
+ UserManager manager = getContext().createContextAsUser(
+ UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0)
+ .getSystemService(UserManager.class);
+ List<UserHandle> enabledProfiles = manager.getEnabledProfiles();
+ if (enabledProfiles.isEmpty()) {
return;
}
Intent intent = new Intent(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
intent.putExtra(Intent.EXTRA_PACKAGES, pkgNames);
intent.putExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE, type);
- for (int userId : userIds) {
- getContext().sendBroadcastAsUser(intent, UserHandle.of(userId),
+ for (UserHandle userHandle : enabledProfiles) {
+ getContext().sendBroadcastAsUser(intent, userHandle,
android.Manifest.permission.RECEIVE_MEDIA_RESOURCE_USAGE);
}
} finally {
diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java
index 20df271a1de2..69c57a9a5d74 100644
--- a/services/core/java/com/android/server/media/MediaShellCommand.java
+++ b/services/core/java/com/android/server/media/MediaShellCommand.java
@@ -38,6 +38,7 @@ import android.view.KeyEvent;
import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.List;
@@ -53,11 +54,13 @@ public class MediaShellCommand extends ShellCommand {
private ISessionManager mSessionService;
private PrintWriter mWriter;
private PrintWriter mErrorWriter;
+ private InputStream mInput;
@Override
public int onCommand(String cmd) {
mWriter = getOutPrintWriter();
mErrorWriter = getErrPrintWriter();
+ mInput = getRawInputStream();
if (TextUtils.isEmpty(cmd)) {
return handleDefaultCommands(cmd);
@@ -189,6 +192,10 @@ public class MediaShellCommand extends ShellCommand {
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
}
+ void log(String code, String msg) {
+ mWriter.println(code + " " + msg);
+ }
+
void showError(String errMsg) {
onHelp();
mErrorWriter.println(errMsg);
@@ -273,11 +280,14 @@ public class MediaShellCommand extends ShellCommand {
cbThread.start();
try {
- InputStreamReader converter = new InputStreamReader(System.in);
+ InputStreamReader converter = new InputStreamReader(mInput);
BufferedReader in = new BufferedReader(converter);
String line;
- while ((line = in.readLine()) != null) {
+ while (true) {
+ mWriter.flush();
+ mErrorWriter.flush();
+ if ((line = in.readLine()) == null) break;
boolean addNewline = true;
if (line.length() <= 0) {
addNewline = false;
@@ -297,7 +307,7 @@ public class MediaShellCommand extends ShellCommand {
synchronized (this) {
if (addNewline) {
- System.out.println("");
+ mWriter.println("");
}
printUsageMessage();
}
diff --git a/services/core/java/com/android/server/media/VolumeCtrl.java b/services/core/java/com/android/server/media/VolumeCtrl.java
index 7a2666566ea2..d516d963e866 100644
--- a/services/core/java/com/android/server/media/VolumeCtrl.java
+++ b/services/core/java/com/android/server/media/VolumeCtrl.java
@@ -32,6 +32,8 @@ import com.android.internal.os.BaseCommand;
public class VolumeCtrl {
private static final String TAG = "VolumeCtrl";
+ private static final String LOG_V = "[V]";
+ private static final String LOG_E = "[E]";
// --stream affects --set, --adj or --get options.
// --show affects --set and --adj options.
@@ -80,21 +82,22 @@ public class VolumeCtrl {
break;
case "--get":
doGet = true;
- log(LOG_V, "will get volume");
+ cmd.log(LOG_V, "will get volume");
break;
case "--stream":
stream = Integer.decode(cmd.getNextArgRequired()).intValue();
- log(LOG_V, "will control stream=" + stream + " (" + streamName(stream) + ")");
+ cmd.log(LOG_V,
+ "will control stream=" + stream + " (" + streamName(stream) + ")");
break;
case "--set":
volIndex = Integer.decode(cmd.getNextArgRequired()).intValue();
mode = VOLUME_CONTROL_MODE_SET;
- log(LOG_V, "will set volume to index=" + volIndex);
+ cmd.log(LOG_V, "will set volume to index=" + volIndex);
break;
case "--adj":
mode = VOLUME_CONTROL_MODE_ADJUST;
adjustment = cmd.getNextArgRequired();
- log(LOG_V, "will adjust volume");
+ cmd.log(LOG_V, "will adjust volume");
break;
default:
throw new IllegalArgumentException("Unknown argument " + option);
@@ -122,11 +125,11 @@ public class VolumeCtrl {
//----------------------------------------
// Test initialization
- log(LOG_V, "Connecting to AudioService");
+ cmd.log(LOG_V, "Connecting to AudioService");
IAudioService audioService = IAudioService.Stub.asInterface(ServiceManager.checkService(
Context.AUDIO_SERVICE));
if (audioService == null) {
- System.err.println(BaseCommand.NO_SYSTEM_ERROR_CODE);
+ cmd.log(LOG_E, BaseCommand.NO_SYSTEM_ERROR_CODE);
throw new AndroidException(
"Can't connect to audio service; is the system running?");
}
@@ -152,23 +155,12 @@ public class VolumeCtrl {
audioService.adjustStreamVolume(stream, adjDir, flag, pack);
}
if (doGet) {
- log(LOG_V, "volume is " + audioService.getStreamVolume(stream)
+ cmd.log(LOG_V, "volume is " + audioService.getStreamVolume(stream)
+ " in range [" + audioService.getStreamMinVolume(stream)
+ ".." + audioService.getStreamMaxVolume(stream) + "]");
}
}
- //--------------------------------------------
- // Utilities
-
- static final String LOG_V = "[v]";
- static final String LOG_W = "[w]";
- static final String LOG_OK = "[ok]";
-
- static void log(String code, String msg) {
- System.out.println(code + " " + msg);
- }
-
static String streamName(int stream) {
try {
return AudioSystem.STREAM_NAMES[stream];
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 2acc60db52e3..0450e5bfd011 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -96,7 +96,9 @@ import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.readStringAttribute;
+import static com.android.internal.util.XmlUtils.readThisIntArrayXml;
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntArrayXml;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
@@ -229,6 +231,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.StatLogger;
+import com.android.internal.util.XmlUtils;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -239,6 +242,7 @@ import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.File;
@@ -313,7 +317,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final int VERSION_ADDED_NETWORK_ID = 9;
private static final int VERSION_SWITCH_UID = 10;
private static final int VERSION_ADDED_CYCLE = 11;
- private static final int VERSION_LATEST = VERSION_ADDED_CYCLE;
+ private static final int VERSION_ADDED_NETWORK_TYPES = 12;
+ private static final int VERSION_LATEST = VERSION_ADDED_NETWORK_TYPES;
@VisibleForTesting
public static final int TYPE_WARNING = SystemMessage.NOTE_NET_WARNING;
@@ -332,6 +337,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final String TAG_WHITELIST = "whitelist";
private static final String TAG_RESTRICT_BACKGROUND = "restrict-background";
private static final String TAG_REVOKED_RESTRICT_BACKGROUND = "revoked-restrict-background";
+ private static final String TAG_XML_UTILS_INT_ARRAY = "int-array";
private static final String ATTR_VERSION = "version";
private static final String ATTR_RESTRICT_BACKGROUND = "restrictBackground";
@@ -360,6 +366,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final String ATTR_USAGE_BYTES = "usageBytes";
private static final String ATTR_USAGE_TIME = "usageTime";
private static final String ATTR_OWNER_PACKAGE = "ownerPackage";
+ private static final String ATTR_NETWORK_TYPES = "networkTypes";
+ private static final String ATTR_XML_UTILS_NAME = "name";
private static final String ACTION_ALLOW_BACKGROUND =
"com.android.server.net.action.ALLOW_BACKGROUND";
@@ -2317,13 +2325,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
final int subId = readIntAttribute(in, ATTR_SUB_ID);
+ final String ownerPackage = readStringAttribute(in, ATTR_OWNER_PACKAGE);
+
+ if (version >= VERSION_ADDED_NETWORK_TYPES) {
+ final int depth = in.getDepth();
+ while (XmlUtils.nextElementWithin(in, depth)) {
+ if (TAG_XML_UTILS_INT_ARRAY.equals(in.getName())
+ && ATTR_NETWORK_TYPES.equals(
+ readStringAttribute(in, ATTR_XML_UTILS_NAME))) {
+ final int[] networkTypes =
+ readThisIntArrayXml(in, TAG_XML_UTILS_INT_ARRAY, null);
+ builder.setNetworkTypes(networkTypes);
+ }
+ }
+ }
+
final SubscriptionPlan plan = builder.build();
mSubscriptionPlans.put(subId, ArrayUtils.appendElement(
SubscriptionPlan.class, mSubscriptionPlans.get(subId), plan));
-
- final String ownerPackage = readStringAttribute(in, ATTR_OWNER_PACKAGE);
mSubscriptionPlansOwner.put(subId, ownerPackage);
-
} else if (TAG_UID_POLICY.equals(tag)) {
final int uid = readIntAttribute(in, ATTR_UID);
final int policy = readIntAttribute(in, ATTR_POLICY);
@@ -2519,6 +2539,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
writeIntAttribute(out, ATTR_LIMIT_BEHAVIOR, plan.getDataLimitBehavior());
writeLongAttribute(out, ATTR_USAGE_BYTES, plan.getDataUsageBytes());
writeLongAttribute(out, ATTR_USAGE_TIME, plan.getDataUsageTime());
+ try {
+ writeIntArrayXml(plan.getNetworkTypes(), ATTR_NETWORK_TYPES, out);
+ } catch (XmlPullParserException ignored) { }
out.endTag(null, TAG_SUBSCRIPTION_PLAN);
}
}
@@ -3316,7 +3339,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// let in core system components (like the Settings app).
final String ownerPackage = mSubscriptionPlansOwner.get(subId);
if (Objects.equals(ownerPackage, callingPackage)
- || (UserHandle.getCallingAppId() == android.os.Process.SYSTEM_UID)) {
+ || (UserHandle.getCallingAppId() == android.os.Process.SYSTEM_UID)
+ || (UserHandle.getCallingAppId() == android.os.Process.PHONE_UID)) {
return mSubscriptionPlans.get(subId);
} else {
Log.w(TAG, "Not returning plans because caller " + callingPackage
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 18da33ce19e0..7257f522221f 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -40,15 +40,12 @@ import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.LinkedList;
-import java.util.concurrent.TimeUnit;
+import java.util.Set;
/**
* Provides an interface to write and query for notification history data for a user from a Protocol
@@ -173,8 +170,8 @@ public class NotificationHistoryDatabase {
mFileWriteHandler.post(rnr);
}
- public void deleteConversation(String pkg, String conversationId) {
- RemoveConversationRunnable rcr = new RemoveConversationRunnable(pkg, conversationId);
+ public void deleteConversations(String pkg, Set<String> conversationIds) {
+ RemoveConversationRunnable rcr = new RemoveConversationRunnable(pkg, conversationIds);
mFileWriteHandler.post(rcr);
}
@@ -467,12 +464,12 @@ public class NotificationHistoryDatabase {
final class RemoveConversationRunnable implements Runnable {
private String mPkg;
- private String mConversationId;
+ private Set<String> mConversationIds;
private NotificationHistory mNotificationHistory;
- public RemoveConversationRunnable(String pkg, String conversationId) {
+ public RemoveConversationRunnable(String pkg, Set<String> conversationIds) {
mPkg = pkg;
- mConversationId = conversationId;
+ mConversationIds = conversationIds;
}
@VisibleForTesting
@@ -482,10 +479,10 @@ public class NotificationHistoryDatabase {
@Override
public void run() {
- if (DEBUG) Slog.d(TAG, "RemoveConversationRunnable " + mPkg + " " + mConversationId);
+ if (DEBUG) Slog.d(TAG, "RemoveConversationRunnable " + mPkg + " " + mConversationIds);
synchronized (mLock) {
// Remove from pending history
- mBuffer.removeConversationFromWrite(mPkg, mConversationId);
+ mBuffer.removeConversationsFromWrite(mPkg, mConversationIds);
Iterator<AtomicFile> historyFileItr = mHistoryFiles.iterator();
while (historyFileItr.hasNext()) {
@@ -496,7 +493,8 @@ public class NotificationHistoryDatabase {
: new NotificationHistory();
readLocked(af, notificationHistory,
new NotificationHistoryFilter.Builder().build());
- if(notificationHistory.removeConversationFromWrite(mPkg, mConversationId)) {
+ if (notificationHistory.removeConversationsFromWrite(
+ mPkg, mConversationIds)) {
writeLocked(af, notificationHistory);
}
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 69a7ce90f1c6..cf3530bfe7fc 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -38,12 +38,12 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FunctionalUtils;
import com.android.server.IoThread;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* Keeps track of per-user notification histories.
@@ -167,7 +167,7 @@ public class NotificationHistoryManager {
}
}
- public void deleteConversation(String pkg, int uid, String conversationId) {
+ public void deleteConversations(String pkg, int uid, Set<String> conversationIds) {
synchronized (mLock) {
int userId = UserHandle.getUserId(uid);
final NotificationHistoryDatabase userHistory =
@@ -179,7 +179,7 @@ public class NotificationHistoryManager {
+ userId);
return;
}
- userHistory.deleteConversation(pkg, conversationId);
+ userHistory.deleteConversations(pkg, conversationIds);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index c301cd2339ec..affdcea1960b 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -19,6 +19,8 @@ package com.android.server.notification;
import android.app.Notification;
import android.app.NotificationChannel;
+import java.util.Set;
+
public interface NotificationManagerInternal {
NotificationChannel getNotificationChannel(String pkg, int uid, String channelId);
void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
@@ -28,5 +30,5 @@ public interface NotificationManagerInternal {
void removeForegroundServiceFlagFromNotification(String pkg, int notificationId, int userId);
- void onConversationRemoved(String pkg, int uid, String conversationId);
+ void onConversationRemoved(String pkg, int uid, Set<String> shortcuts);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 79882dab48a1..b4c98e06a442 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5605,8 +5605,8 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public void onConversationRemoved(String pkg, int uid, String conversationId) {
- onConversationRemovedInternal(pkg, uid, conversationId);
+ public void onConversationRemoved(String pkg, int uid, Set<String> shortcuts) {
+ onConversationRemovedInternal(pkg, uid, shortcuts);
}
@GuardedBy("mNotificationLock")
@@ -5835,14 +5835,13 @@ public class NotificationManagerService extends SystemService {
mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground));
}
- private void onConversationRemovedInternal(String pkg, int uid, String conversationId) {
+ private void onConversationRemovedInternal(String pkg, int uid, Set<String> shortcuts) {
checkCallerIsSystem();
Preconditions.checkStringNotEmpty(pkg);
- Preconditions.checkStringNotEmpty(conversationId);
- mHistoryManager.deleteConversation(pkg, uid, conversationId);
+ mHistoryManager.deleteConversations(pkg, uid, shortcuts);
List<String> deletedChannelIds =
- mPreferencesHelper.deleteConversation(pkg, uid, conversationId);
+ mPreferencesHelper.deleteConversations(pkg, uid, shortcuts);
for (String channelId : deletedChannelIds) {
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
UserHandle.getUserId(uid), REASON_CHANNEL_BANNED,
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 9cf9545d889a..bdf98f41cbbc 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -79,6 +79,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class PreferencesHelper implements RankingConfig {
@@ -1428,7 +1429,8 @@ public class PreferencesHelper implements RankingConfig {
}
}
- public @NonNull List<String> deleteConversation(String pkg, int uid, String conversationId) {
+ public @NonNull List<String> deleteConversations(String pkg, int uid,
+ Set<String> conversationIds) {
synchronized (mPackagePreferences) {
List<String> deletedChannelIds = new ArrayList<>();
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
@@ -1438,7 +1440,8 @@ public class PreferencesHelper implements RankingConfig {
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
- if (conversationId.equals(nc.getConversationId())) {
+ if (nc.getConversationId() != null
+ && conversationIds.contains(nc.getConversationId())) {
nc.setDeleted(true);
LogMaker lm = getChannelLog(nc, pkg);
lm.setType(
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 60737bf77c48..949dcb254d21 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2081,7 +2081,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mRelinquished = true;
- return mPm.new VerificationParams(user, stageDir, localObserver, params,
+ // TODO(b/169375643): Remove this workaround once b/161121612 is fixed.
+ PackageInstaller.SessionParams copiedParams = params.copy();
+ if (params.isStaged) {
+ // This is called by the pre-reboot verification. Don't enable rollback here since
+ // it has been enabled when pre-reboot verification starts.
+ copiedParams.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
+ }
+ return mPm.new VerificationParams(user, stageDir, localObserver, copiedParams,
mInstallSource, mInstallerUid, mSigningDetails, sessionId);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b451eaf198f8..1ae1681f6771 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6214,8 +6214,11 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
final AndroidPackage p = mPackages.get(packageName);
+ if (p == null) {
+ return false;
+ }
final PackageSetting ps = getPackageSetting(p.getPackageName());
- if (p == null || ps == null) {
+ if (ps == null) {
return false;
}
final int callingUid = Binder.getCallingUid();
@@ -11291,6 +11294,8 @@ public class PackageManagerService extends IPackageManager.Stub
mSettings.addRenamedPackageLPw(parsedPackage.getRealPackage(),
originalPkgSetting.name);
mTransferredPackages.add(originalPkgSetting.name);
+ } else {
+ mSettings.removeRenamedPackageLPw(parsedPackage.getPackageName());
}
}
if (pkgSetting.sharedUser != null) {
@@ -25947,6 +25952,15 @@ public class PackageManagerService extends IPackageManager.Stub
mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.writeLPr();
}
+
+ @Override
+ public void holdLock(int durationMs) {
+ mContext.enforceCallingPermission(
+ Manifest.permission.INJECT_EVENTS, "holdLock requires shell identity");
+ synchronized (mLock) {
+ SystemClock.sleep(durationMs);
+ }
+ }
}
interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index bae36b2ad353..a922d76cf9eb 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -485,6 +485,10 @@ public final class Settings {
return mRenamedPackages.put(pkgName, origPkgName);
}
+ void removeRenamedPackageLPw(String pkgName) {
+ mRenamedPackages.remove(pkgName);
+ }
+
public boolean canPropagatePermissionToInstantApp(String permName) {
return mPermissions.canPropagatePermissionToInstantApp(permName);
}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 865b8a1e97eb..524f9acf15cd 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -425,8 +425,7 @@ public final class BasePermission {
public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg,
UidPermissionState uidState) {
- int index = pkg.getRequestedPermissions().indexOf(name);
- if (!uidState.hasRequestedPermission(name) && index == -1) {
+ if (!uidState.hasPermissionState(name) && !pkg.getRequestedPermissions().contains(name)) {
throw new SecurityException("Package " + pkg.getPackageName()
+ " has not requested permission " + name);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 840b233902f6..474ce7c32c5a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -943,7 +943,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private boolean checkSinglePermissionInternal(int uid,
@NonNull UidPermissionState uidState, @NonNull String permissionName) {
- if (!uidState.hasPermission(permissionName)) {
+ if (!uidState.isPermissionGranted(permissionName)) {
return false;
}
@@ -1659,7 +1659,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
// Permission is already revoked, no need to do anything.
- if (!uidState.hasPermission(permName)) {
+ if (!uidState.isPermissionGranted(permName)) {
return;
}
@@ -2482,12 +2482,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return Collections.emptySet();
}
if (!ps.getInstantApp(userId)) {
- return uidState.getPermissions();
+ return uidState.getGrantedPermissions();
} else {
// Install permission state is shared among all users, but instant app state is
// per-user, so we can only filter it here unless we make install permission state
// per-user as well.
- final Set<String> instantPermissions = new ArraySet<>(uidState.getPermissions());
+ final Set<String> instantPermissions = new ArraySet<>(uidState.getGrantedPermissions());
instantPermissions.removeIf(permissionName -> {
BasePermission permission = mSettings.getPermission(permissionName);
if (permission == null) {
@@ -2660,7 +2660,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Cache newImplicitPermissions before modifing permissionsState as for the shared
// uids the original and new state are the same object
- if (!origState.hasRequestedPermission(permName)
+ if (!origState.hasPermissionState(permName)
&& (pkg.getImplicitPermissions().contains(permName)
|| (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
if (pkg.getImplicitPermissions().contains(permName)) {
@@ -2685,7 +2685,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
String splitPermName = sp.getSplitPermission();
if (sp.getNewPermissions().contains(permName)
- && origState.hasPermission(splitPermName)) {
+ && origState.isPermissionGranted(splitPermName)) {
upgradedActivityRecognitionPermission = splitPermName;
newImplicitPermissions.add(permName);
@@ -2741,7 +2741,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- if (grant == GRANT_INSTALL && !allowedSig && !origState.hasPermission(perm)) {
+ if (grant == GRANT_INSTALL && !allowedSig && !origState.isPermissionGranted(perm)) {
// If this is an existing, non-system package, then
// we can't add any new permissions to it. Runtime
// permissions can be added any time - they are dynamic.
@@ -2849,7 +2849,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- if (!uidState.hasPermission(bp.name)
+ if (!uidState.isPermissionGranted(bp.name)
&& uidState.grantPermission(bp)
!= PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
@@ -2993,7 +2993,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
boolean supportsRuntimePermissions = pkg.getTargetSdkVersion()
>= Build.VERSION_CODES.M;
- for (String permission : ps.getPermissions()) {
+ for (String permission : ps.getGrantedPermissions()) {
if (!pkg.getImplicitPermissions().contains(permission)) {
BasePermission bp = mSettings.getPermissionLocked(permission);
if (bp.isRuntime()) {
@@ -3050,7 +3050,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
int numSourcePerm = sourcePerms.size();
for (int i = 0; i < numSourcePerm; i++) {
String sourcePerm = sourcePerms.valueAt(i);
- if (ps.hasPermission(sourcePerm)) {
+ if (ps.isPermissionGranted(sourcePerm)) {
if (!isGranted) {
flags = 0;
}
@@ -3155,7 +3155,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
- if (!origPs.hasRequestedPermission(sourcePerms)) {
+ if (!origPs.hasPermissionState(sourcePerms)) {
boolean inheritsFromInstallPerm = false;
for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
sourcePermNum++) {
@@ -3465,7 +3465,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (!allowed && bp.isDevelopment()) {
// For development permissions, a development permission
// is granted only if it was already granted.
- allowed = origPermissions.hasPermission(perm);
+ allowed = origPermissions.isPermissionGranted(perm);
}
if (!allowed && bp.isSetup()
&& ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
@@ -3687,7 +3687,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
continue;
}
- if (uidState.hasPermission(permissionName)) {
+ if (uidState.isPermissionGranted(permissionName)) {
if (oldGrantedRestrictedPermissions.get(userId) == null) {
oldGrantedRestrictedPermissions.put(userId, new ArraySet<>());
}
@@ -3749,7 +3749,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// as whitelisting trumps policy i.e. policy cannot grant a non
// grantable permission.
if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- final boolean isGranted = uidState.hasPermission(permissionName);
+ final boolean isGranted = uidState.isPermissionGranted(permissionName);
if (!isWhitelisted && isGranted) {
mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -3791,7 +3791,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
+ " and user " + userId);
continue;
}
- if (!newUidState.hasPermission(permission)) {
+ if (!newUidState.isPermissionGranted(permission)) {
callback.onPermissionRevoked(pkg.getUid(), userId, null);
break;
}
diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
index b45176b720b5..06a7f8dbd2be 100644
--- a/services/core/java/com/android/server/pm/permission/UidPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
@@ -54,149 +54,161 @@ public final class UidPermissionState {
@NonNull
private final Object mLock = new Object();
+ private boolean mMissing;
+
@GuardedBy("mLock")
+ @Nullable
private ArrayMap<String, PermissionState> mPermissions;
+ private boolean mPermissionReviewRequired;
+
@NonNull
private int[] mGlobalGids = NO_GIDS;
- private boolean mMissing;
+ public UidPermissionState() {}
- private boolean mPermissionReviewRequired;
+ public UidPermissionState(@NonNull UidPermissionState other) {
+ synchronized (mLock) {
+ mMissing = other.mMissing;
- public UidPermissionState() {
- /* do nothing */
- }
+ if (other.mPermissions != null) {
+ mPermissions = new ArrayMap<>();
+ final int permissionsSize = other.mPermissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ final String name = other.mPermissions.keyAt(i);
+ final PermissionState permissionState = other.mPermissions.valueAt(i);
+ mPermissions.put(name, new PermissionState(permissionState));
+ }
+ }
+
+ mPermissionReviewRequired = other.mPermissionReviewRequired;
- public UidPermissionState(@NonNull UidPermissionState prototype) {
- copyFrom(prototype);
+ if (other.mGlobalGids != NO_GIDS) {
+ mGlobalGids = other.mGlobalGids.clone();
+ }
+ }
}
/**
- * Gets the global gids, applicable to all users.
+ * Reset the internal state of this object.
*/
- @NonNull
- public int[] getGlobalGids() {
- return mGlobalGids;
+ public void reset() {
+ synchronized (mLock) {
+ mMissing = false;
+ mPermissions = null;
+ mPermissionReviewRequired = false;
+ mGlobalGids = NO_GIDS;
+ invalidateCache();
+ }
}
/**
- * Sets the global gids, applicable to all users.
- *
- * @param globalGids The global gids.
+ * Check whether the permissions state is missing for a user. This can happen if permission
+ * state is rolled back and we'll need to generate a reasonable default state to keep the app
+ * usable.
*/
- public void setGlobalGids(@NonNull int[] globalGids) {
- if (!ArrayUtils.isEmpty(globalGids)) {
- mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
- }
+ public boolean isMissing() {
+ return mMissing;
}
- static void invalidateCache() {
- PackageManager.invalidatePackageInfoCache();
+ /**
+ * Set whether the permissions state is missing for a user. This can happen if permission state
+ * is rolled back and we'll need to generate a reasonable default state to keep the app usable.
+ */
+ public void setMissing(boolean missing) {
+ mMissing = missing;
}
/**
- * Initialized this instance from another one.
+ * Get whether there is a permission state for a permission.
*
- * @param other The other instance.
+ * @deprecated This used to be named hasRequestedPermission() and its usage is confusing
*/
- public void copyFrom(@NonNull UidPermissionState other) {
- if (other == this) {
- return;
+ @Deprecated
+ public boolean hasPermissionState(@NonNull String name) {
+ synchronized (mLock) {
+ return mPermissions != null && mPermissions.containsKey(name);
}
+ }
+ /**
+ * Get whether there is a permission state for any of the permissions.
+ *
+ * @deprecated This used to be named hasRequestedPermission() and its usage is confusing
+ */
+ @Deprecated
+ public boolean hasPermissionState(@NonNull ArraySet<String> names) {
synchronized (mLock) {
- if (mPermissions != null) {
- if (other.mPermissions == null) {
- mPermissions = null;
- } else {
- mPermissions.clear();
- }
+ if (mPermissions == null) {
+ return false;
}
- if (other.mPermissions != null) {
- if (mPermissions == null) {
- mPermissions = new ArrayMap<>();
- }
- final int permissionCount = other.mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- String name = other.mPermissions.keyAt(i);
- PermissionState permissionState = other.mPermissions.valueAt(i);
- mPermissions.put(name, new PermissionState(permissionState));
+ final int namesSize = names.size();
+ for (int i = 0; i < namesSize; i++) {
+ final String name = names.valueAt(i);
+ if (mPermissions.containsKey(name)) {
+ return true;
}
}
- }
-
- mGlobalGids = NO_GIDS;
- if (other.mGlobalGids != NO_GIDS) {
- mGlobalGids = other.mGlobalGids.clone();
- }
-
- mMissing = other.mMissing;
-
- mPermissionReviewRequired = other.mPermissionReviewRequired;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
return false;
}
- if (getClass() != obj.getClass()) {
- return false;
- }
- final UidPermissionState other = (UidPermissionState) obj;
+ }
+ /**
+ * Gets the state for a permission or null if none.
+ *
+ * @param name the permission name.
+ * @return the permission state.
+ */
+ @Nullable
+ public PermissionState getPermissionState(@NonNull String name) {
synchronized (mLock) {
if (mPermissions == null) {
- if (other.mPermissions != null) {
- return false;
- }
- } else if (!mPermissions.equals(other.mPermissions)) {
- return false;
+ return null;
}
+ return mPermissions.get(name);
}
-
- if (mMissing != other.mMissing) {
- return false;
- }
-
- if (mPermissionReviewRequired != other.mPermissionReviewRequired) {
- return false;
- }
- return Arrays.equals(mGlobalGids, other.mGlobalGids);
}
/**
- * Check whether the permissions state is missing for a user. This can happen if permission
- * state is rolled back and we'll need to generate a reasonable default state to keep the app
- * usable.
+ * Get all permission states.
+ *
+ * @return the permission states
*/
- public boolean isMissing() {
- return mMissing;
+ @NonNull
+ public List<PermissionState> getPermissionStates() {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return Collections.emptyList();
+ }
+ return new ArrayList<>(mPermissions.values());
+ }
}
/**
- * Set whether the permissions state is missing for a user. This can happen if permission state
- * is rolled back and we'll need to generate a reasonable default state to keep the app usable.
+ * Put a permission state.
*/
- public void setMissing(boolean missing) {
- mMissing = missing;
- }
-
- public boolean isPermissionReviewRequired() {
- return mPermissionReviewRequired;
+ public void putPermissionState(@NonNull BasePermission permission, boolean isGranted,
+ int flags) {
+ synchronized (mLock) {
+ ensureNoPermissionState(permission.name);
+ PermissionState permissionState = ensurePermissionState(permission);
+ if (isGranted) {
+ permissionState.grant();
+ }
+ permissionState.updateFlags(flags, flags);
+ if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ mPermissionReviewRequired = true;
+ }
+ }
}
/**
- * Gets whether the state has a given permission.
+ * Get whether a permission is granted.
*
- * @param name The permission name.
- * @return Whether the state has the permission.
+ * @param name the permission name
+ * @return whether the permission is granted
*/
- public boolean hasPermission(@NonNull String name) {
+ public boolean isPermissionGranted(@NonNull String name) {
synchronized (mLock) {
if (mPermissions == null) {
return false;
@@ -207,86 +219,105 @@ public final class UidPermissionState {
}
/**
- * Returns whether the state has any known request for the given permission name,
- * whether or not it has been granted.
+ * Get all the granted permissions.
*
- * @deprecated Not all requested permissions may be here.
+ * @return the granted permissions
*/
- @Deprecated
- public boolean hasRequestedPermission(@NonNull ArraySet<String> names) {
+ @NonNull
+ public Set<String> getGrantedPermissions() {
synchronized (mLock) {
if (mPermissions == null) {
- return false;
+ return Collections.emptySet();
}
- for (int i = names.size() - 1; i >= 0; i--) {
- if (mPermissions.get(names.valueAt(i)) != null) {
- return true;
+
+ Set<String> permissions = new ArraySet<>(mPermissions.size());
+ final int permissionsSize = mPermissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ PermissionState permissionState = mPermissions.valueAt(i);
+
+ if (permissionState.isGranted()) {
+ permissions.add(permissionState.getName());
}
}
+ return permissions;
}
-
- return false;
}
/**
- * Returns whether the state has any known request for the given permission name,
- * whether or not it has been granted.
+ * Grant a permission.
*
- * @deprecated Not all requested permissions may be here.
+ * @param permission the permission to grantt
+ * @return the operation result, which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
*/
- @Deprecated
- public boolean hasRequestedPermission(@NonNull String name) {
- return mPermissions != null && (mPermissions.get(name) != null);
+ public int grantPermission(@NonNull BasePermission permission) {
+ if (isPermissionGranted(permission.getName())) {
+ return PERMISSION_OPERATION_SUCCESS;
+ }
+
+ PermissionState permissionState = ensurePermissionState(permission);
+
+ if (!permissionState.grant()) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED
+ : PERMISSION_OPERATION_SUCCESS;
}
/**
- * Gets all permissions for a given device user id regardless if they
- * are install time or runtime permissions.
+ * Revoke a permission.
*
- * @return The permissions or an empty set.
+ * @param permission the permission to revoke
+ * @return the operation result, which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
*/
- @NonNull
- public Set<String> getPermissions() {
- synchronized (mLock) {
- if (mPermissions == null) {
- return Collections.emptySet();
- }
-
- Set<String> permissions = new ArraySet<>(mPermissions.size());
+ public int revokePermission(@NonNull BasePermission permission) {
+ final String name = permission.getName();
+ if (!isPermissionGranted(name)) {
+ return PERMISSION_OPERATION_SUCCESS;
+ }
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- String permission = mPermissions.keyAt(i);
+ PermissionState permissionState;
+ synchronized (mLock) {
+ permissionState = mPermissions.get(name);
+ }
- if (hasPermission(permission)) {
- permissions.add(permission);
- }
- }
+ if (!permissionState.revoke()) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
- return permissions;
+ if (permissionState.isDefault()) {
+ ensureNoPermissionState(name);
}
+
+ return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED
+ : PERMISSION_OPERATION_SUCCESS;
}
/**
- * Gets the flags for a permission.
+ * Get the flags for a permission.
*
- * @param name The permission name.
- * @return The permission state or null if no such.
+ * @param name the permission name.
+ * @return the permission flags
*/
public int getPermissionFlags(@NonNull String name) {
- PermissionState permState = getPermissionState(name);
- if (permState != null) {
- return permState.getFlags();
+ final PermissionState permissionState = getPermissionState(name);
+ if (permissionState == null) {
+ return 0;
}
- return 0;
+ return permissionState.getFlags();
}
/**
- * Update the flags associated with a given permission.
- * @param permission The permission whose flags to update.
- * @param flagMask Mask for which flags to change.
- * @param flagValues New values for the mask flags.
- * @return Whether the permission flags changed.
+ * Update the flags for a permission.
+ *
+ * @param permission the permission name
+ * @param flagMask the mask for the flags
+ * @param flagValues the new values for the masked flags
+ * @return whether the permission flags changed
*/
public boolean updatePermissionFlags(@NonNull BasePermission permission, int flagMask,
int flagValues) {
@@ -294,11 +325,10 @@ public final class UidPermissionState {
return false;
}
- PermissionState permissionState = ensurePermissionState(permission);
-
- final int oldFlags = permissionState.getFlags();
-
synchronized (mLock) {
+ final PermissionState permissionState = ensurePermissionState(permission);
+ final int oldFlags = permissionState.getFlags();
+
final boolean updated = permissionState.updateFlags(flagMask, flagValues);
if (updated) {
final int newFlags = permissionState.getFlags();
@@ -316,199 +346,132 @@ public final class UidPermissionState {
}
}
- private boolean hasPermissionRequiringReview() {
- synchronized (mLock) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- final PermissionState permission = mPermissions.valueAt(i);
- if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- return true;
- }
- }
- }
- return false;
- }
-
public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) {
synchronized (mLock) {
if (mPermissions == null) {
return false;
}
boolean changed = false;
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- PermissionState permissionState = mPermissions.valueAt(i);
+ final int permissionsSize = mPermissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ final PermissionState permissionState = mPermissions.valueAt(i);
changed |= permissionState.updateFlags(flagMask, flagValues);
}
return changed;
}
}
- /**
- * Compute the Linux gids for a given device user from the permissions
- * granted to this user. Note that these are computed to avoid additional
- * state as they are rarely accessed.
- *
- * @param userId The device user id.
- * @return The gids for the device user.
- */
@NonNull
- public int[] computeGids(@UserIdInt int userId) {
- int[] gids = mGlobalGids;
-
+ private PermissionState ensurePermissionState(@NonNull BasePermission permission) {
+ final String name = permission.getName();
synchronized (mLock) {
- if (mPermissions != null) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- PermissionState permissionState = mPermissions.valueAt(i);
- if (!permissionState.isGranted()) {
- continue;
- }
- final int[] permGids = permissionState.computeGids(userId);
- if (permGids != NO_GIDS) {
- gids = appendInts(gids, permGids);
- }
- }
+ if (mPermissions == null) {
+ mPermissions = new ArrayMap<>();
}
+ PermissionState permissionState = mPermissions.get(name);
+ if (permissionState == null) {
+ permissionState = new PermissionState(permission);
+ mPermissions.put(name, permissionState);
+ }
+ return permissionState;
}
-
- return gids;
- }
-
- /**
- * Compute the Linux gids for all device users from the permissions
- * granted to these users.
- *
- * @return The gids for all device users.
- */
- @NonNull
- public int[] computeGids(@NonNull int[] userIds) {
- int[] gids = mGlobalGids;
-
- for (int userId : userIds) {
- final int[] userGids = computeGids(userId);
- gids = appendInts(gids, userGids);
- }
-
- return gids;
}
- /**
- * Resets the internal state of this object.
- */
- public void reset() {
- mGlobalGids = NO_GIDS;
-
+ private void ensureNoPermissionState(@NonNull String name) {
synchronized (mLock) {
- mPermissions = null;
- invalidateCache();
+ if (mPermissions == null) {
+ return;
+ }
+ mPermissions.remove(name);
+ if (mPermissions.isEmpty()) {
+ mPermissions = null;
+ }
}
+ }
- mMissing = false;
- mPermissionReviewRequired = false;
+ public boolean isPermissionReviewRequired() {
+ return mPermissionReviewRequired;
}
- /**
- * Gets the state for a permission or null if no such.
- *
- * @param name The permission name.
- * @return The permission state.
- */
- @Nullable
- public PermissionState getPermissionState(@NonNull String name) {
+ private boolean hasPermissionRequiringReview() {
synchronized (mLock) {
- if (mPermissions == null) {
- return null;
+ final int permissionsSize = mPermissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ final PermissionState permission = mPermissions.valueAt(i);
+ if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ return true;
+ }
}
- return mPermissions.get(name);
+ return false;
}
}
/**
- * Gets all permission states.
- *
- * @return The permission states or an empty set.
+ * Gets the global gids, applicable to all users.
*/
@NonNull
- public List<PermissionState> getPermissionStates() {
- synchronized (mLock) {
- if (mPermissions == null) {
- return Collections.emptyList();
- }
- return new ArrayList<>(mPermissions.values());
- }
+ public int[] getGlobalGids() {
+ return mGlobalGids;
}
/**
- * Put a permission state.
+ * Sets the global gids, applicable to all users.
+ *
+ * @param globalGids The global gids.
*/
- public void putPermissionState(@NonNull BasePermission permission, boolean isGranted,
- int flags) {
- synchronized (mLock) {
- ensureNoPermissionState(permission.name);
- PermissionState permissionState = ensurePermissionState(permission);
- if (isGranted) {
- permissionState.grant();
- }
- permissionState.updateFlags(flags, flags);
- if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- mPermissionReviewRequired = true;
- }
+ public void setGlobalGids(@NonNull int[] globalGids) {
+ if (!ArrayUtils.isEmpty(globalGids)) {
+ mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
+ } else {
+ mGlobalGids = NO_GIDS;
}
}
/**
- * Grant a permission.
+ * Compute the Linux GIDs from the permissions granted to a user.
*
- * @param permission The permission to grant.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
- * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
- * #PERMISSION_OPERATION_FAILURE}.
+ * @param userId the user ID
+ * @return the GIDs for the user
*/
- public int grantPermission(@NonNull BasePermission permission) {
- if (hasPermission(permission.getName())) {
- return PERMISSION_OPERATION_SUCCESS;
- }
-
- PermissionState permissionState = ensurePermissionState(permission);
+ @NonNull
+ public int[] computeGids(@UserIdInt int userId) {
+ int[] gids = mGlobalGids;
- if (!permissionState.grant()) {
- return PERMISSION_OPERATION_FAILURE;
+ synchronized (mLock) {
+ if (mPermissions != null) {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ PermissionState permissionState = mPermissions.valueAt(i);
+ if (!permissionState.isGranted()) {
+ continue;
+ }
+ final int[] permGids = permissionState.computeGids(userId);
+ if (permGids != NO_GIDS) {
+ gids = appendInts(gids, permGids);
+ }
+ }
+ }
}
- return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED
- : PERMISSION_OPERATION_SUCCESS;
+ return gids;
}
/**
- * Revoke a permission.
+ * Compute the Linux GIDs from the permissions granted to specified users.
*
- * @param permission The permission to revoke.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
- * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
- * #PERMISSION_OPERATION_FAILURE}.
+ * @param userIds the user IDs
+ * @return the GIDs for the user
*/
- public int revokePermission(@NonNull BasePermission permission) {
- final String permissionName = permission.getName();
- if (!hasPermission(permissionName)) {
- return PERMISSION_OPERATION_SUCCESS;
- }
-
- PermissionState permissionState;
- synchronized (mLock) {
- permissionState = mPermissions.get(permissionName);
- }
-
- if (!permissionState.revoke()) {
- return PERMISSION_OPERATION_FAILURE;
- }
+ @NonNull
+ public int[] computeGids(@NonNull int[] userIds) {
+ int[] gids = mGlobalGids;
- if (permissionState.isDefault()) {
- ensureNoPermissionState(permissionName);
+ for (final int userId : userIds) {
+ final int[] userGids = computeGids(userId);
+ gids = appendInts(gids, userGids);
}
- return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED
- : PERMISSION_OPERATION_SUCCESS;
+ return gids;
}
// TODO: fix this to use arraycopy and append all ints in one go
@@ -521,31 +484,7 @@ public final class UidPermissionState {
return current;
}
- @NonNull
- private PermissionState ensurePermissionState(@NonNull BasePermission permission) {
- final String permissionName = permission.getName();
- synchronized (mLock) {
- if (mPermissions == null) {
- mPermissions = new ArrayMap<>();
- }
- PermissionState permissionState = mPermissions.get(permissionName);
- if (permissionState == null) {
- permissionState = new PermissionState(permission);
- mPermissions.put(permissionName, permissionState);
- }
- return permissionState;
- }
- }
-
- private void ensureNoPermissionState(@NonNull String name) {
- synchronized (mLock) {
- if (mPermissions == null) {
- return;
- }
- mPermissions.remove(name);
- if (mPermissions.isEmpty()) {
- mPermissions = null;
- }
- }
+ static void invalidateCache() {
+ PackageManager.invalidatePackageInfoCache();
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 35b14490eba2..df283e210be8 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1552,6 +1552,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
startActivityAsUser(intent, UserHandle.CURRENT);
}
+ private void toggleNotificationPanel() {
+ IStatusBarService statusBarService = getStatusBarService();
+ if (statusBarService != null) {
+ try {
+ statusBarService.togglePanel();
+ } catch (RemoteException e) {
+ // do nothing.
+ }
+ }
+ }
+
private void showPictureInPictureMenu(KeyEvent event) {
if (DEBUG_INPUT) Log.d(TAG, "showPictureInPictureMenu event=" + event);
mHandler.removeMessages(MSG_SHOW_PICTURE_IN_PICTURE_MENU);
@@ -1696,14 +1707,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
launchAssistAction(null, deviceId);
break;
case LONG_PRESS_HOME_NOTIFICATION_PANEL:
- IStatusBarService statusBarService = getStatusBarService();
- if (statusBarService != null) {
- try {
- statusBarService.togglePanel();
- } catch (RemoteException e) {
- // do nothing.
- }
- }
+ toggleNotificationPanel();
break;
default:
Log.w(TAG, "Undefined long press on home behavior: "
@@ -2807,6 +2811,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
msg.sendToTarget();
}
return -1;
+ } else if (keyCode == KeyEvent.KEYCODE_NOTIFICATION) {
+ if (!down) {
+ toggleNotificationPanel();
+ }
+ return -1;
}
// Toggle Caps Lock on META-ALT.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 6044ee18614a..60da8e5c7b70 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -5408,6 +5408,22 @@ public final class PowerManagerService extends SystemService
}
@Override // Binder call
+ public boolean isAmbientDisplaySuppressedForTokenByApp(@NonNull String token, int appUid) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_DREAM_STATE, null);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_DREAM_SUPPRESSION, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return isAmbientDisplayAvailable()
+ && mAmbientDisplaySuppressionController.isSuppressed(token, appUid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public boolean isAmbientDisplaySuppressed() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_DREAM_STATE, null);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 8ccbbdd2265c..cfceaabfc229 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -22,7 +22,9 @@ import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -33,6 +35,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -51,6 +54,7 @@ import android.media.tv.ITvInputService;
import android.media.tv.ITvInputServiceCallback;
import android.media.tv.ITvInputSession;
import android.media.tv.ITvInputSessionCallback;
+import android.media.tv.TvChannelInfo;
import android.media.tv.TvContentRating;
import android.media.tv.TvContentRatingSystemInfo;
import android.media.tv.TvContract;
@@ -78,6 +82,7 @@ import android.util.SparseArray;
import android.view.InputChannel;
import android.view.Surface;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
@@ -108,6 +113,9 @@ public final class TvInputManagerService extends SystemService {
private static final boolean DEBUG = false;
private static final String TAG = "TvInputManagerService";
private static final String DVB_DIRECTORY = "/dev/dvb";
+ private static final int APP_TAG_SELF = TvChannelInfo.APP_TAG_SELF;
+ private static final String PERMISSION_ACCESS_WATCHED_PROGRAMS =
+ "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS";
// There are two different formats of DVB frontend devices. One is /dev/dvb%d.frontend%d,
// another one is /dev/dvb/adapter%d/frontend%d. Followings are the patterns for selecting the
@@ -136,6 +144,8 @@ public final class TvInputManagerService extends SystemService {
private final WatchLogHandler mWatchLogHandler;
+ private final ActivityManager mActivityManager;
+
public TvInputManagerService(Context context) {
super(context);
@@ -144,6 +154,9 @@ public final class TvInputManagerService extends SystemService {
IoThread.get().getLooper());
mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
+ mActivityManager =
+ (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
+
synchronized (mLock) {
getOrCreateUserStateLocked(mCurrentUserId);
}
@@ -686,14 +699,18 @@ public final class TvInputManagerService extends SystemService {
SessionState sessionState = null;
try {
sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+ UserState userState = getOrCreateUserStateLocked(userId);
if (sessionState.session != null) {
- UserState userState = getOrCreateUserStateLocked(userId);
if (sessionToken == userState.mainSessionToken) {
setMainLocked(sessionToken, false, callingUid, userId);
}
sessionState.session.asBinder().unlinkToDeath(sessionState, 0);
sessionState.session.release();
}
+ sessionState.isCurrent = false;
+ sessionState.currentChannel = null;
+ notifyCurrentChannelInfosUpdatedLocked(
+ userState, getCurrentTvChannelInfosInternalLocked(userState));
} catch (RemoteException | SessionNotFoundException e) {
Slog.e(TAG, "error in releaseSession", e);
} finally {
@@ -834,6 +851,22 @@ public final class TvInputManagerService extends SystemService {
}
}
+ private void notifyCurrentChannelInfosUpdatedLocked(
+ UserState userState, List<TvChannelInfo> infos) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyCurrentChannelInfosUpdatedLocked");
+ }
+ int n = userState.mCallbacks.beginBroadcast();
+ for (int i = 0; i < n; ++i) {
+ try {
+ userState.mCallbacks.getBroadcastItem(i).onCurrentTvChannelInfosUpdated(infos);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "failed to report updated current channel infos to callback", e);
+ }
+ }
+ userState.mCallbacks.finishBroadcast();
+ }
+
private void updateTvInputInfoLocked(UserState userState, TvInputInfo inputInfo) {
if (DEBUG) {
Slog.d(TAG, "updateTvInputInfoLocked(inputInfo=" + inputInfo + ")");
@@ -1394,13 +1427,19 @@ public final class TvInputManagerService extends SystemService {
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
channelUri, params);
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ SessionState sessionState = userState.sessionStateMap.get(sessionToken);
+ if (sessionState != null) {
+ sessionState.isCurrent = true;
+ sessionState.currentChannel = channelUri;
+ notifyCurrentChannelInfosUpdatedLocked(
+ userState, getCurrentTvChannelInfosInternalLocked(userState));
+ }
if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
// Do not log the watch history for passthrough inputs.
return;
}
- UserState userState = getOrCreateUserStateLocked(resolvedUserId);
- SessionState sessionState = userState.sessionStateMap.get(sessionToken);
if (sessionState.isRecordingSession) {
return;
}
@@ -2049,6 +2088,21 @@ public final class TvInputManagerService extends SystemService {
return clientPid;
}
+ @Override
+ public List<TvChannelInfo> getCurrentTvChannelInfos(@UserIdInt int userId) {
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, "getTvCurrentChannelInfos");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ return getCurrentTvChannelInfosInternalLocked(userState);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
/**
* Add a hardware device in the TvInputHardwareManager for CTS testing
* purpose.
@@ -2219,6 +2273,69 @@ public final class TvInputManagerService extends SystemService {
}
}
+ private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked(UserState userState) {
+ List<TvChannelInfo> channelInfos = new ArrayList<>();
+ boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission();
+ for (SessionState state : userState.sessionStateMap.values()) {
+ if (state.isCurrent) {
+ Integer appTag;
+ int appType;
+ if (state.callingUid == Binder.getCallingUid()) {
+ appTag = APP_TAG_SELF;
+ appType = TvChannelInfo.APP_TYPE_SELF;
+ } else {
+ appTag = userState.mAppTagMap.get(state.callingUid);
+ if (appTag == null) {
+ appTag = userState.mNextAppTag++;
+ userState.mAppTagMap.put(state.callingUid, appTag);
+ }
+ appType = isSystemApp(state.componentName.getPackageName())
+ ? TvChannelInfo.APP_TYPE_SYSTEM
+ : TvChannelInfo.APP_TYPE_NON_SYSTEM;
+ }
+ channelInfos.add(new TvChannelInfo(
+ state.inputId,
+ watchedProgramsAccess ? state.currentChannel : null,
+ state.isRecordingSession,
+ isForeground(state.callingPid),
+ appType,
+ appTag));
+ }
+ }
+ return channelInfos;
+ }
+
+ private boolean isForeground(int pid) {
+ if (mActivityManager == null) {
+ return false;
+ }
+ List<RunningAppProcessInfo> appProcesses = mActivityManager.getRunningAppProcesses();
+ if (appProcesses == null) {
+ return false;
+ }
+ for (RunningAppProcessInfo appProcess : appProcesses) {
+ if (appProcess.pid == pid
+ && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasAccessWatchedProgramsPermission() {
+ return mContext.checkCallingPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean isSystemApp(String pkg) {
+ try {
+ return (mContext.getPackageManager().getApplicationInfo(pkg, 0).flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0;
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+
private static final class UserState {
// A mapping from the TV input id to its TvInputState.
private Map<String, TvInputState> inputMap = new HashMap<>();
@@ -2250,6 +2367,11 @@ public final class TvInputManagerService extends SystemService {
// service.
private final PersistentDataStore persistentDataStore;
+ @GuardedBy("mLock")
+ private final Map<Integer, Integer> mAppTagMap = new HashMap<>();
+ @GuardedBy("mLock")
+ private int mNextAppTag = 1;
+
private UserState(Context context, int userId) {
persistentDataStore = new PersistentDataStore(context, userId);
}
@@ -2336,6 +2458,9 @@ public final class TvInputManagerService extends SystemService {
// Not null if this session represents an external device connected to a hardware TV input.
private IBinder hardwareSessionToken;
+ private boolean isCurrent = false;
+ private Uri currentChannel = null;
+
private SessionState(IBinder sessionToken, String inputId, ComponentName componentName,
boolean isRecordingSession, ITvInputClient client, int seq, int callingUid,
int callingPid, int userId, String sessionId) {
@@ -2584,6 +2709,11 @@ public final class TvInputManagerService extends SystemService {
if (mSessionState.session == null || mSessionState.client == null) {
return;
}
+ mSessionState.isCurrent = true;
+ mSessionState.currentChannel = channelUri;
+ UserState userState = getOrCreateUserStateLocked(mSessionState.userId);
+ notifyCurrentChannelInfosUpdatedLocked(
+ userState, getCurrentTvChannelInfosInternalLocked(userState));
try {
// TODO: Consider adding this channel change in the watch log. When we do
// that, how we can protect the watch log from malicious tv inputs should
diff --git a/services/core/java/com/android/server/utils/XmlName.java b/services/core/java/com/android/server/utils/XmlName.java
new file mode 100644
index 000000000000..c0e22daaa32e
--- /dev/null
+++ b/services/core/java/com/android/server/utils/XmlName.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specify the XML name for the annotated field or persistence class.
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target({ ElementType.FIELD, ElementType.TYPE })
+public @interface XmlName {
+ String value();
+}
diff --git a/services/core/java/com/android/server/utils/XmlPersistence.java b/services/core/java/com/android/server/utils/XmlPersistence.java
new file mode 100644
index 000000000000..8900a9d4783e
--- /dev/null
+++ b/services/core/java/com/android/server/utils/XmlPersistence.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Generate XML persistence for the annotated class.
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
+public @interface XmlPersistence {
+ /**
+ * Name for the generated XML persistence class.
+ * <p>
+ * The name defaults to the target class name appended with {@code Persistence} if unspecified.
+ */
+ String value();
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index adf330f0db45..1cb610fc7604 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4465,6 +4465,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
detachChildren();
}
+ if (app != null) {
+ app.invalidateOomScoreReferenceState(false /* computeNow */);
+ }
switch (state) {
case RESUMED:
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 2dc22ecfc022..e65be41f44cd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -499,9 +499,6 @@ public abstract class ActivityTaskManagerInternal {
/** @return the process for the top-most resumed activity in the system. */
public abstract WindowProcessController getTopApp();
- /** Generate oom-score-adjustment rank for all tasks in the system based on z-order. */
- public abstract void rankTaskLayersIfNeeded();
-
/** Destroy all activities. */
public abstract void scheduleDestroyAllActivities(String reason);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 15669cbe227a..0402140c6fc1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -666,7 +666,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
*/
CompatModePackages mCompatModePackages;
- private FontScaleSettingObserver mFontScaleSettingObserver;
+ private SettingObserver mSettingsObserver;
WindowOrganizerController mWindowOrganizerController;
TaskOrganizerController mTaskOrganizerController;
@@ -676,16 +676,19 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private int mDeviceOwnerUid = Process.INVALID_UID;
- private final class FontScaleSettingObserver extends ContentObserver {
+ private final class SettingObserver extends ContentObserver {
private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE);
private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS);
+ private final Uri mForceBoldTextUri = Settings.Secure.getUriFor(
+ Settings.Secure.FORCE_BOLD_TEXT);
- public FontScaleSettingObserver() {
+ SettingObserver() {
super(mH);
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(mFontScaleUri, false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(mHideErrorDialogsUri, false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(mForceBoldTextUri, false, this, UserHandle.USER_ALL);
}
@Override
@@ -698,6 +701,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
updateShouldShowDialogsLocked(getGlobalConfiguration());
}
+ } else if (mForceBoldTextUri.equals(uri)) {
+ updateForceBoldTextIfNeeded(userId);
}
}
}
@@ -757,7 +762,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
public void installSystemProviders() {
- mFontScaleSettingObserver = new FontScaleSettingObserver();
+ mSettingsObserver = new SettingObserver();
}
public void retrieveSettings(ContentResolver resolver) {
@@ -5362,6 +5367,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
+ private void updateForceBoldTextIfNeeded(@UserIdInt int userId) {
+ final int forceBoldTextConfig = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FORCE_BOLD_TEXT, Configuration.FORCE_BOLD_TEXT_UNDEFINED, userId);
+ synchronized (mGlobalLock) {
+ if (getGlobalConfiguration().forceBoldText == forceBoldTextConfig) {
+ return;
+ }
+ final Configuration configuration =
+ mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
+ configuration.forceBoldText = forceBoldTextConfig;
+ updatePersistentConfiguration(configuration, userId);
+ }
+ }
+
// Actually is sleeping or shutting down or whatever else in the future
// is an inactive state.
boolean isSleepingOrShuttingDownLocked() {
@@ -7187,16 +7206,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
- @HotPath(caller = HotPath.OOM_ADJUSTMENT)
- @Override
- public void rankTaskLayersIfNeeded() {
- synchronized (mGlobalLockWithoutBoost) {
- if (mRootWindowContainer != null) {
- mRootWindowContainer.rankTaskLayersIfNeeded();
- }
- }
- }
-
@Override
public void scheduleDestroyAllActivities(String reason) {
synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b3e69d4847f7..1153c4666fbe 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -193,7 +193,6 @@ import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
-import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
@@ -552,11 +551,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
private SurfaceControl mParentSurfaceControl;
private InputWindowHandle mPortalWindowHandle;
- // Last systemUiVisibility we received from status bar.
- private int mLastStatusBarVisibility = 0;
- // Last systemUiVisibility we dispatched to windows.
- private int mLastDispatchedSystemUiVisibility = 0;
-
/** Corner radius that windows should have in order to match the display. */
private final float mWindowCornerRadius;
@@ -2907,10 +2901,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
pw.print(" mLastFocus="); pw.println(mLastFocus);
}
pw.print(" mFocusedApp="); pw.println(mFocusedApp);
- if (mLastStatusBarVisibility != 0) {
- pw.print(" mLastStatusBarVisibility=0x");
- pw.println(Integer.toHexString(mLastStatusBarVisibility));
- }
if (mFixedRotationLaunchingApp != null) {
pw.println(" mFixedRotationLaunchingApp=" + mFixedRotationLaunchingApp);
}
@@ -3770,50 +3760,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return win != null;
}
- void hideTransientBars() {
- // TODO(b/118118435): Remove this after migration
- final int transientFlags = View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
- statusBarVisibilityChanged(mLastStatusBarVisibility & ~transientFlags);
-
- getInsetsPolicy().hideTransient();
- }
-
- void statusBarVisibilityChanged(int visibility) {
- mLastStatusBarVisibility = visibility;
- updateStatusBarVisibilityLocked(visibility);
- }
-
- private boolean updateStatusBarVisibilityLocked(int visibility) {
- if (mLastDispatchedSystemUiVisibility == visibility) {
- return false;
- }
- final int globalDiff = (visibility ^ mLastDispatchedSystemUiVisibility)
- // We are only interested in differences of one of the
- // clearable flags...
- & View.SYSTEM_UI_CLEARABLE_FLAGS
- // ...if it has actually been cleared.
- & ~visibility;
-
- mLastDispatchedSystemUiVisibility = visibility;
- if (isDefaultDisplay) {
- mWmService.mInputManager.setSystemUiVisibility(visibility);
- }
- updateSystemUiVisibility(visibility, globalDiff);
- return true;
- }
-
- void updateSystemUiVisibility(int visibility, int globalDiff) {
- forAllWindows(w -> {
- final int curValue = w.mSystemUiVisibility;
- final int diff = (curValue ^ visibility) & globalDiff;
- final int newValue = (curValue & ~diff) | (visibility & diff);
- if (newValue != curValue) {
- w.mSeq++;
- w.mSystemUiVisibility = newValue;
- }
- }, true /* traverseTopToBottom */);
- }
-
void onWindowFreezeTimeout() {
Slog.w(TAG_WM, "Window freeze timeout expired.");
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 6fffde14cf7d..dce798e81a86 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -29,6 +29,8 @@ import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
@@ -41,6 +43,7 @@ import static android.view.InsetsState.ITYPE_TOP_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;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE;
@@ -154,6 +157,7 @@ import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.Surface;
import android.view.View;
+import android.view.ViewDebug;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Side.InsetsSide;
import android.view.WindowInsets.Type;
@@ -215,7 +219,8 @@ public class DisplayPolicy {
/** Use the transit animation in style resource (see {@link #selectAnimation}). */
static final int ANIMATION_STYLEABLE = 0;
- private static final int[] SHOW_TYPES_FOR_SWIPE = {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR};
+ private static final int[] SHOW_TYPES_FOR_SWIPE = {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR,
+ ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR};
private static final int[] SHOW_TYPES_FOR_PANIC = {ITYPE_NAVIGATION_BAR};
private final WindowManagerService mService;
@@ -301,6 +306,16 @@ public class DisplayPolicy {
private WindowState mNavigationBarAlt = null;
@WindowManagerPolicy.AltBarPosition
private int mNavigationBarAltPosition = ALT_BAR_UNKNOWN;
+ // Alternative climate bar for when flexible insets mapping is used to place a climate bar on
+ // the screen.
+ private WindowState mClimateBarAlt = null;
+ @WindowManagerPolicy.AltBarPosition
+ private int mClimateBarAltPosition = ALT_BAR_UNKNOWN;
+ // Alternative extra nav bar for when flexible insets mapping is used to place an extra nav bar
+ // on the screen.
+ private WindowState mExtraNavBarAlt = null;
+ @WindowManagerPolicy.AltBarPosition
+ private int mExtraNavBarAltPosition = ALT_BAR_UNKNOWN;
/** See {@link #getNavigationBarFrameHeight} */
private int[] mNavigationBarFrameHeightForRotationDefault = new int[4];
@@ -669,6 +684,12 @@ public class DisplayPolicy {
if (mNavigationBarAlt != null && mNavigationBarAltPosition == pos) {
requestTransientBars(mNavigationBarAlt);
}
+ if (mClimateBarAlt != null && mClimateBarAltPosition == pos) {
+ requestTransientBars(mClimateBarAlt);
+ }
+ if (mExtraNavBarAlt != null && mExtraNavBarAltPosition == pos) {
+ requestTransientBars(mExtraNavBarAlt);
+ }
}
void systemReady() {
@@ -936,6 +957,12 @@ public class DisplayPolicy {
if (mNavigationBarAlt == win) {
mNavigationBarAltPosition = getAltBarPosition(attrs);
}
+ if (mClimateBarAlt == win) {
+ mClimateBarAltPosition = getAltBarPosition(attrs);
+ }
+ if (mExtraNavBarAlt == win) {
+ mExtraNavBarAltPosition = getAltBarPosition(attrs);
+ }
}
/**
@@ -1033,6 +1060,16 @@ public class DisplayPolicy {
return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
break;
+ case ITYPE_CLIMATE_BAR:
+ if (mClimateBarAlt != null && mClimateBarAlt.isAlive()) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ break;
+ case ITYPE_EXTRA_NAVIGATION_BAR:
+ if (mExtraNavBarAlt != null && mExtraNavBarAlt.isAlive()) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ break;
}
}
}
@@ -1146,6 +1183,14 @@ public class DisplayPolicy {
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;
}
mDisplayContent.setInsetProvider(insetsType, win, null);
}
@@ -1194,6 +1239,8 @@ public class DisplayPolicy {
switch (insetsType) {
case ITYPE_NAVIGATION_BAR:
case ITYPE_STATUS_BAR:
+ case ITYPE_CLIMATE_BAR:
+ case ITYPE_EXTRA_NAVIGATION_BAR:
case ITYPE_CAPTION_BAR:
if (++count > 1) {
throw new IllegalArgumentException(
@@ -1223,6 +1270,12 @@ public class DisplayPolicy {
if (mDisplayContent.isDefaultDisplay) {
mService.mPolicy.setKeyguardCandidateLw(null);
}
+ } else if (mClimateBarAlt == win) {
+ mClimateBarAlt = null;
+ mDisplayContent.setInsetProvider(ITYPE_CLIMATE_BAR, null, null);
+ } else if (mExtraNavBarAlt == win) {
+ mExtraNavBarAlt = null;
+ mDisplayContent.setInsetProvider(ITYPE_EXTRA_NAVIGATION_BAR, null, null);
}
if (mLastFocusedWindow == win) {
mLastFocusedWindow = null;
@@ -1311,7 +1364,8 @@ public class DisplayPolicy {
return R.anim.dock_left_enter;
}
}
- } else if (win == mStatusBarAlt || win == mNavigationBarAlt) {
+ } else if (win == mStatusBarAlt || win == mNavigationBarAlt || win == mClimateBarAlt
+ || win == mExtraNavBarAlt) {
if (win.getAttrs().windowAnimations != 0) {
return ANIMATION_STYLEABLE;
}
@@ -1393,9 +1447,9 @@ public class DisplayPolicy {
boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout) {
- final int fl = PolicyControl.getWindowFlags(null, attrs);
+ final int fl = attrs.flags;
final int pfl = attrs.privateFlags;
- final int sysUiVis = PolicyControl.getSystemUiVisibility(null, attrs);
+ final int sysUiVis = attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility;
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0;
final boolean layoutInScreenAndInsetDecor = layoutInScreen
@@ -1949,7 +2003,7 @@ public class DisplayPolicy {
final WindowManager.LayoutParams attrs = win.getAttrs();
final int type = attrs.type;
- final int fl = PolicyControl.getWindowFlags(win, attrs);
+ final int fl = attrs.flags;
final int pfl = attrs.privateFlags;
final int sim = attrs.softInputMode;
@@ -2191,7 +2245,7 @@ public class DisplayPolicy {
final boolean affectsSystemUi = win.canAffectSystemUiFlags();
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi);
mService.mPolicy.applyKeyguardPolicyLw(win, imeTarget);
- final int fl = PolicyControl.getWindowFlags(win, attrs);
+ final int fl = attrs.flags;
if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi
&& attrs.type == TYPE_INPUT_METHOD) {
mForcingShowNavBar = true;
@@ -2366,8 +2420,7 @@ public class DisplayPolicy {
return false;
}
final LayoutParams attrs = mTopFullscreenOpaqueWindowState.getAttrs();
- final int fl = PolicyControl.getWindowFlags(null, attrs);
- final int sysui = PolicyControl.getSystemUiVisibility(null, attrs);
+ final int fl = attrs.flags;
final InsetsSource request = mTopFullscreenOpaqueWindowState.getRequestedInsetsState()
.peekSource(ITYPE_STATUS_BAR);
if (WindowManagerDebugConfig.DEBUG) {
@@ -2810,10 +2863,19 @@ public class DisplayPolicy {
}
final InsetsState requestedState = controlTarget.getRequestedInsetsState();
- final @InsetsType int restorePositionTypes = (requestedState.getSourceOrDefaultVisibility(
- ITYPE_NAVIGATION_BAR) ? Type.navigationBars() : 0) | (
- requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR) ? Type.statusBars()
- : 0);
+ final @InsetsType int restorePositionTypes =
+ (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
+ ? Type.navigationBars() : 0)
+ | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
+ ? Type.statusBars() : 0)
+ | (mExtraNavBarAlt != null
+ && requestedState.getSourceOrDefaultVisibility(
+ ITYPE_EXTRA_NAVIGATION_BAR)
+ ? Type.navigationBars() : 0)
+ | (mClimateBarAlt != null
+ && requestedState.getSourceOrDefaultVisibility(
+ ITYPE_CLIMATE_BAR)
+ ? Type.statusBars() : 0);
if (swipeTarget == mNavigationBar
&& (restorePositionTypes & Type.navigationBars()) != 0) {
@@ -2891,9 +2953,9 @@ public class DisplayPolicy {
mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
- final int fullscreenAppearance = updateLightStatusBarLw(0 /* vis */,
+ final int fullscreenAppearance = updateLightStatusBarLw(0 /* appearance */,
mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
- final int dockedAppearance = updateLightStatusBarLw(0 /* vis */,
+ final int dockedAppearance = updateLightStatusBarLw(0 /* appearance */,
mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
final boolean inSplitScreen =
mService.mRoot.getDefaultTaskDisplayArea().isSplitScreenModeActivated();
@@ -2964,6 +3026,10 @@ public class DisplayPolicy {
}
});
+ if (mDisplayContent.isDefaultDisplay) {
+ mService.mInputManager.setSystemUiLightsOut(
+ isFullscreen || (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0);
+ }
return true;
}
@@ -2976,9 +3042,7 @@ public class DisplayPolicy {
// If the top fullscreen-or-dimming window is also the top fullscreen, respect
// its light flag.
appearance &= ~APPEARANCE_LIGHT_STATUS_BARS;
- final int legacyAppearance = InsetsFlags.getAppearance(
- PolicyControl.getSystemUiVisibility(statusColorWin, null));
- appearance |= (statusColorWin.mAttrs.insetsFlags.appearance | legacyAppearance)
+ appearance |= statusColorWin.mAttrs.insetsFlags.appearance
& APPEARANCE_LIGHT_STATUS_BARS;
} else if (statusColorWin.isDimming()) {
// Otherwise if it's dimming, clear the light flag.
@@ -3001,8 +3065,8 @@ public class DisplayPolicy {
final boolean imeWindowCanNavColorWindow = imeWindow != null
&& imeWindow.isVisibleLw()
&& navBarPosition == NAV_BAR_BOTTOM
- && (PolicyControl.getWindowFlags(imeWindow, null)
- & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+ && (imeWindow.mAttrs.flags
+ & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
if (opaque != null && opaqueOrDimming == opaque) {
// If the top fullscreen-or-dimming window is also the top fullscreen, respect it
@@ -3024,7 +3088,7 @@ public class DisplayPolicy {
// The IME window and the dimming window are competing. Check if the dimming window can be
// IME target or not.
- if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) {
+ if (LayoutParams.mayUseInputMethod(opaqueOrDimming.mAttrs.flags)) {
// The IME window is above the dimming window.
return imeWindow;
} else {
@@ -3298,22 +3362,30 @@ public class DisplayPolicy {
pw.print(prefix); pw.print("mHdmiPlugged="); pw.println(mHdmiPlugged);
if (mLastDisableFlags != 0) {
pw.print(prefix); pw.print("mLastDisableFlags=0x");
- pw.print(Integer.toHexString(mLastDisableFlags));
+ pw.println(Integer.toHexString(mLastDisableFlags));
+ }
+ if (mLastAppearance != 0) {
+ pw.print(prefix); pw.print("mLastAppearance=");
+ pw.println(ViewDebug.flagsToString(InsetsFlags.class, "appearance", mLastAppearance));
+ }
+ if (mLastBehavior != 0) {
+ pw.print(prefix); pw.print("mLastBehavior=");
+ pw.println(ViewDebug.flagsToString(InsetsFlags.class, "behavior", mLastBehavior));
}
pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
- pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
+ pw.print(" mDreamingLockscreen="); pw.println(mDreamingLockscreen);
if (mStatusBar != null) {
- pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar);
+ pw.print(prefix); pw.print("mStatusBar="); pw.println(mStatusBar);
}
if (mStatusBarAlt != null) {
- pw.print(prefix); pw.print("mStatusBarAlt="); pw.print(mStatusBarAlt);
+ pw.print(prefix); pw.print("mStatusBarAlt="); pw.println(mStatusBarAlt);
pw.print(prefix); pw.print("mStatusBarAltPosition=");
pw.println(mStatusBarAltPosition);
}
if (mNotificationShade != null) {
- pw.print(prefix); pw.print("mExpandedPanel="); pw.print(mNotificationShade);
+ pw.print(prefix); pw.print("mExpandedPanel="); pw.println(mNotificationShade);
}
- pw.print(" isKeyguardShowing="); pw.println(isKeyguardShowing());
+ pw.print(prefix); pw.print("isKeyguardShowing="); pw.println(isKeyguardShowing());
if (mNavigationBar != null) {
pw.print(prefix); pw.print("mNavigationBar="); pw.println(mNavigationBar);
pw.print(prefix); pw.print("mNavBarOpacityMode="); pw.println(mNavBarOpacityMode);
@@ -3326,6 +3398,16 @@ public class DisplayPolicy {
pw.print(prefix); pw.print("mNavigationBarAltPosition=");
pw.println(mNavigationBarAltPosition);
}
+ if (mClimateBarAlt != null) {
+ pw.print(prefix); pw.print("mClimateBarAlt="); pw.println(mClimateBarAlt);
+ pw.print(prefix); pw.print("mClimateBarAltPosition=");
+ pw.println(mClimateBarAltPosition);
+ }
+ if (mExtraNavBarAlt != null) {
+ pw.print(prefix); pw.print("mExtraNavBarAlt="); pw.println(mExtraNavBarAlt);
+ pw.print(prefix); pw.print("mExtraNavBarAltPosition=");
+ pw.println(mExtraNavBarAltPosition);
+ }
if (mFocusedWindow != null) {
pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow);
}
@@ -3344,9 +3426,9 @@ public class DisplayPolicy {
}
pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen);
pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
- pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars");
- pw.print(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
+ pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars=");
+ pw.println(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
pw.print(prefix); pw.println("Looper state:");
mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index f0f338534ed2..1d8cdf7e773c 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -138,8 +138,9 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
&& dcTarget.getParentWindow() == mImeTargetFromIme
&& dcTarget.mSubLayer > mImeTargetFromIme.getWindow().mSubLayer)
|| mImeTargetFromIme == mDisplayContent.getImeFallback()
+ || mImeTargetFromIme == mDisplayContent.mInputMethodInputTarget
|| controlTarget == mImeTargetFromIme
- && (mImeTargetFromIme.getWindow() == null
+ && (mImeTargetFromIme.getWindow() == null
|| !mImeTargetFromIme.getWindow().isClosing());
}
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 8b1a0c93cfa3..02dad3978d42 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -28,6 +28,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
import android.os.Binder;
@@ -46,6 +47,7 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
import android.view.animation.Animation;
@@ -134,11 +136,8 @@ public class ImmersiveModeConfirmation {
boolean userSetupComplete, boolean navBarEmpty) {
mHandler.removeMessages(H.SHOW);
if (isImmersiveMode) {
- final boolean disabled = PolicyControl.disableImmersiveConfirmation(pkg);
- if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s sConfirmed=%s",
- disabled, sConfirmed));
- if (!disabled
- && (DEBUG_SHOW_EVERY_TIME || !sConfirmed)
+ if (DEBUG) Slog.d(TAG, "immersiveModeChanged() sConfirmed=" + sConfirmed);
+ if ((DEBUG_SHOW_EVERY_TIME || !sConfirmed)
&& userSetupComplete
&& !mVrModeEnabled
&& !navBarEmpty
@@ -339,6 +338,13 @@ public class ImmersiveModeConfirmation {
public boolean onTouchEvent(MotionEvent motion) {
return true;
}
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ // we will be hiding the nav bar, so layout as if it's already hidden
+ return new WindowInsets.Builder(insets).setInsets(
+ Type.systemBars(), Insets.NONE).build();
+ }
}
/**
@@ -359,10 +365,6 @@ public class ImmersiveModeConfirmation {
mClingWindow = new ClingWindowView(mContext, mConfirm);
- // we will be hiding the nav bar, so layout as if it's already hidden
- mClingWindow.setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
-
// show the confirmation
WindowManager.LayoutParams lp = getClingWindowLayoutParams();
getWindowManager().addView(mClingWindow, lp);
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index a7f32c09b83b..5bed1862a629 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -93,6 +95,8 @@ class InsetsSourceProvider {
case ITYPE_STATUS_BAR:
case ITYPE_NAVIGATION_BAR:
case ITYPE_IME:
+ case ITYPE_CLIMATE_BAR:
+ case ITYPE_EXTRA_NAVIGATION_BAR:
mControllable = true;
break;
default:
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index c0bdbff6b761..b59452ffaf36 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_INVALID;
@@ -42,6 +43,7 @@ import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -115,7 +117,7 @@ class InsetsStateController {
}
InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
- final @InternalInsetsType int type = getInsetsTypeForWindowType(attrs.type);
+ final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs);
final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
final @WindowingMode int windowingMode = token != null
? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
@@ -135,7 +137,9 @@ class InsetsStateController {
return false;
}
- private static @InternalInsetsType int getInsetsTypeForWindowType(int type) {
+ private static @InternalInsetsType
+ int getInsetsTypeForLayoutParams(WindowManager.LayoutParams attrs) {
+ @WindowType int type = attrs.type;
switch (type) {
case TYPE_STATUS_BAR:
return ITYPE_STATUS_BAR;
@@ -143,9 +147,22 @@ class InsetsStateController {
return ITYPE_NAVIGATION_BAR;
case TYPE_INPUT_METHOD:
return ITYPE_IME;
- default:
- return ITYPE_INVALID;
}
+
+ // If not one of the types above, check whether an internal inset mapping is specified.
+ if (attrs.providesInsetsTypes != null) {
+ for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
+ switch (insetsType) {
+ case ITYPE_STATUS_BAR:
+ case ITYPE_NAVIGATION_BAR:
+ case ITYPE_CLIMATE_BAR:
+ case ITYPE_EXTRA_NAVIGATION_BAR:
+ return insetsType;
+ }
+ }
+ }
+
+ return ITYPE_INVALID;
}
/** @see #getInsetsForDispatch */
@@ -158,14 +175,15 @@ class InsetsStateController {
state.removeSource(type);
// Navigation bar doesn't get influenced by anything else
- if (type == ITYPE_NAVIGATION_BAR) {
+ if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
state.removeSource(ITYPE_IME);
state.removeSource(ITYPE_STATUS_BAR);
+ state.removeSource(ITYPE_CLIMATE_BAR);
state.removeSource(ITYPE_CAPTION_BAR);
}
// Status bar doesn't get influenced by caption bar
- if (type == ITYPE_STATUS_BAR) {
+ if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) {
state.removeSource(ITYPE_CAPTION_BAR);
}
@@ -336,8 +354,12 @@ class InsetsStateController {
@Nullable InsetsControlTarget fakeNavControlling) {
onControlChanged(ITYPE_STATUS_BAR, statusControlling);
onControlChanged(ITYPE_NAVIGATION_BAR, navControlling);
+ onControlChanged(ITYPE_CLIMATE_BAR, statusControlling);
+ onControlChanged(ITYPE_EXTRA_NAVIGATION_BAR, navControlling);
onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeStatusControlling);
onControlFakeTargetChanged(ITYPE_NAVIGATION_BAR, fakeNavControlling);
+ onControlFakeTargetChanged(ITYPE_CLIMATE_BAR, fakeStatusControlling);
+ onControlFakeTargetChanged(ITYPE_EXTRA_NAVIGATION_BAR, fakeNavControlling);
notifyPendingInsetsControlChanged();
}
diff --git a/services/core/java/com/android/server/wm/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java
deleted file mode 100644
index 61b6e0b25961..000000000000
--- a/services/core/java/com/android/server/wm/PolicyControl.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.ArraySet;
-import android.util.Slog;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-/**
- * Runtime adjustments applied to the global window policy.
- *
- * This includes forcing immersive mode behavior for one or both system bars (based on a package
- * list) and permanently disabling immersive mode confirmations for specific packages.
- *
- * Control by setting {@link Settings.Global#POLICY_CONTROL} to one or more name-value pairs.
- * e.g.
- * to force immersive mode everywhere:
- * "immersive.full=*"
- * to force transient status for all apps except a specific package:
- * "immersive.status=apps,-com.package"
- * to disable the immersive mode confirmations for specific packages:
- * "immersive.preconfirms=com.package.one,com.package.two"
- *
- * Separate multiple name-value pairs with ':'
- * e.g. "immersive.status=apps:immersive.preconfirms=*"
- */
-class PolicyControl {
- private static final String TAG = "PolicyControl";
- private static final boolean DEBUG = false;
-
- @VisibleForTesting
- static final String NAME_IMMERSIVE_FULL = "immersive.full";
- private static final String NAME_IMMERSIVE_STATUS = "immersive.status";
- private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation";
- private static final String NAME_IMMERSIVE_PRECONFIRMATIONS = "immersive.preconfirms";
-
- private static String sSettingValue;
- private static Filter sImmersivePreconfirmationsFilter;
- private static Filter sImmersiveStatusFilter;
- private static Filter sImmersiveNavigationFilter;
-
- static int getSystemUiVisibility(WindowState win, LayoutParams attrs) {
- attrs = attrs != null ? attrs : win.getAttrs();
- int vis = win != null ? win.getSystemUiVisibility()
- : (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility);
- if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
- vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | View.SYSTEM_UI_FLAG_FULLSCREEN;
- if (attrs.isFullscreen()) {
- vis |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- }
- vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.STATUS_BAR_TRANSLUCENT);
- }
- if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) {
- vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
- if (attrs.isFullscreen()) {
- vis |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- }
- vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.NAVIGATION_BAR_TRANSLUCENT);
- }
- return vis;
- }
-
- static int getWindowFlags(WindowState win, LayoutParams attrs) {
- attrs = attrs != null ? attrs : win.getAttrs();
- int flags = attrs.flags;
- if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
- flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
- flags &= ~(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
- | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
- }
- if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) {
- flags &= ~WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
- }
- return flags;
- }
-
- static int adjustClearableFlags(WindowState win, int clearableFlags) {
- final LayoutParams attrs = win != null ? win.getAttrs() : null;
- if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
- clearableFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
- }
- return clearableFlags;
- }
-
- static boolean disableImmersiveConfirmation(String pkg) {
- return (sImmersivePreconfirmationsFilter != null
- && sImmersivePreconfirmationsFilter.matches(pkg))
- || ActivityManager.isRunningInTestHarness();
- }
-
- static boolean reloadFromSetting(Context context) {
- if (DEBUG) Slog.d(TAG, "reloadFromSetting()");
- String value = null;
- try {
- value = Settings.Global.getStringForUser(context.getContentResolver(),
- Settings.Global.POLICY_CONTROL,
- UserHandle.USER_CURRENT);
- if (sSettingValue == value || sSettingValue != null && sSettingValue.equals(value)) {
- return false;
- }
- setFilters(value);
- sSettingValue = value;
- } catch (Throwable t) {
- Slog.w(TAG, "Error loading policy control, value=" + value, t);
- return false;
- }
- return true;
- }
-
- static void dump(String prefix, PrintWriter pw) {
- dump("sImmersiveStatusFilter", sImmersiveStatusFilter, prefix, pw);
- dump("sImmersiveNavigationFilter", sImmersiveNavigationFilter, prefix, pw);
- dump("sImmersivePreconfirmationsFilter", sImmersivePreconfirmationsFilter, prefix, pw);
- }
-
- private static void dump(String name, Filter filter, String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("PolicyControl."); pw.print(name); pw.print('=');
- if (filter == null) {
- pw.println("null");
- } else {
- filter.dump(pw); pw.println();
- }
- }
-
- @VisibleForTesting
- static void setFilters(String value) {
- if (DEBUG) Slog.d(TAG, "setFilters: " + value);
- sImmersiveStatusFilter = null;
- sImmersiveNavigationFilter = null;
- sImmersivePreconfirmationsFilter = null;
- if (value != null) {
- String[] nvps = value.split(":");
- for (String nvp : nvps) {
- int i = nvp.indexOf('=');
- if (i == -1) continue;
- String n = nvp.substring(0, i);
- String v = nvp.substring(i + 1);
- if (n.equals(NAME_IMMERSIVE_FULL)) {
- Filter f = Filter.parse(v);
- sImmersiveStatusFilter = sImmersiveNavigationFilter = f;
- if (sImmersivePreconfirmationsFilter == null) {
- sImmersivePreconfirmationsFilter = f;
- }
- } else if (n.equals(NAME_IMMERSIVE_STATUS)) {
- Filter f = Filter.parse(v);
- sImmersiveStatusFilter = f;
- } else if (n.equals(NAME_IMMERSIVE_NAVIGATION)) {
- Filter f = Filter.parse(v);
- sImmersiveNavigationFilter = f;
- if (sImmersivePreconfirmationsFilter == null) {
- sImmersivePreconfirmationsFilter = f;
- }
- } else if (n.equals(NAME_IMMERSIVE_PRECONFIRMATIONS)) {
- Filter f = Filter.parse(v);
- sImmersivePreconfirmationsFilter = f;
- }
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter);
- Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter);
- Slog.d(TAG, "immersivePreconfirmationsFilter: " + sImmersivePreconfirmationsFilter);
- }
- }
-
- private static class Filter {
- private static final String ALL = "*";
- private static final String APPS = "apps";
-
- private final ArraySet<String> mAllowlist;
- private final ArraySet<String> mDenylist;
-
- private Filter(ArraySet<String> allowlist, ArraySet<String> denylist) {
- mAllowlist = allowlist;
- mDenylist = denylist;
- }
-
- boolean matches(LayoutParams attrs) {
- if (attrs == null) return false;
- boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
- && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
- if (isApp && mDenylist.contains(APPS)) return false;
- if (onDenylist(attrs.packageName)) return false;
- if (isApp && mAllowlist.contains(APPS)) return true;
- return onAllowlist(attrs.packageName);
- }
-
- boolean matches(String packageName) {
- return !onDenylist(packageName) && onAllowlist(packageName);
- }
-
- private boolean onDenylist(String packageName) {
- return mDenylist.contains(packageName) || mDenylist.contains(ALL);
- }
-
- private boolean onAllowlist(String packageName) {
- return mAllowlist.contains(ALL) || mAllowlist.contains(packageName);
- }
-
- void dump(PrintWriter pw) {
- pw.print("Filter[");
- dump("allowlist", mAllowlist, pw); pw.print(',');
- dump("denylist", mDenylist, pw); pw.print(']');
- }
-
- private void dump(String name, ArraySet<String> set, PrintWriter pw) {
- pw.print(name); pw.print("=(");
- final int n = set.size();
- for (int i = 0; i < n; i++) {
- if (i > 0) pw.print(',');
- pw.print(set.valueAt(i));
- }
- pw.print(')');
- }
-
- @Override
- public String toString() {
- StringWriter sw = new StringWriter();
- dump(new PrintWriter(sw, true));
- return sw.toString();
- }
-
- // value = comma-delimited list of tokens, where token = (package name|apps|*)
- // e.g. "com.package1", or "apps, com.android.keyguard" or "*"
- static Filter parse(String value) {
- if (value == null) return null;
- ArraySet<String> allowlist = new ArraySet<String>();
- ArraySet<String> denylist = new ArraySet<String>();
- for (String token : value.split(",")) {
- token = token.trim();
- if (token.startsWith("-") && token.length() > 1) {
- token = token.substring(1);
- denylist.add(token);
- } else {
- allowlist.add(token);
- }
- }
- return new Filter(allowlist, denylist);
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 71ecf725ab80..8b52b580a091 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -276,6 +276,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Whether tasks have moved and we need to rank the tasks before next OOM scoring
private boolean mTaskLayersChanged = true;
private int mTmpTaskLayerRank;
+ private final ArraySet<WindowProcessController> mTmpTaskLayerChangedProcs = new ArraySet<>();
+ private final LockedScheduler mRankTaskLayersScheduler;
private boolean mTmpBoolean;
private RemoteException mTmpRemoteException;
@@ -450,6 +452,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mStackSupervisor = mService.mStackSupervisor;
mStackSupervisor.mRootWindowContainer = this;
mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl("Display-off");
+ mRankTaskLayersScheduler = new LockedScheduler(mService) {
+ @Override
+ public void execute() {
+ rankTaskLayersIfNeeded();
+ }
+ };
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -2698,27 +2706,39 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
void invalidateTaskLayers() {
mTaskLayersChanged = true;
+ mRankTaskLayersScheduler.scheduleIfNeeded();
}
+ /** Generate oom-score-adjustment rank for all tasks in the system based on z-order. */
void rankTaskLayersIfNeeded() {
if (!mTaskLayersChanged) {
return;
}
mTaskLayersChanged = false;
mTmpTaskLayerRank = 0;
- final PooledConsumer c = PooledLambda.obtainConsumer(
- RootWindowContainer::rankTaskLayerForActivity, this,
- PooledLambda.__(ActivityRecord.class));
- forAllActivities(c);
- c.recycle();
- }
+ // Only rank for leaf tasks because the score of activity is based on immediate parent.
+ forAllLeafTasks(task -> {
+ final int oldRank = task.mLayerRank;
+ final ActivityRecord r = task.topRunningActivityLocked();
+ if (r != null && r.mVisibleRequested) {
+ task.mLayerRank = ++mTmpTaskLayerRank;
+ } else {
+ task.mLayerRank = Task.LAYER_RANK_INVISIBLE;
+ }
+ if (task.mLayerRank != oldRank) {
+ task.forAllActivities(activity -> {
+ if (activity.hasProcess()) {
+ mTmpTaskLayerChangedProcs.add(activity.app);
+ }
+ });
+ }
+ }, true /* traverseTopToBottom */);
- private void rankTaskLayerForActivity(ActivityRecord r) {
- if (r.canBeTopRunning() && r.mVisibleRequested) {
- r.getTask().mLayerRank = ++mTmpTaskLayerRank;
- } else {
- r.getTask().mLayerRank = -1;
+ for (int i = mTmpTaskLayerChangedProcs.size() - 1; i >= 0; i--) {
+ mTmpTaskLayerChangedProcs.valueAt(i).invalidateOomScoreReferenceState(
+ true /* computeNow */);
}
+ mTmpTaskLayerChangedProcs.clear();
}
void clearOtherAppTimeTrackers(AppTimeTracker except) {
@@ -3680,4 +3700,34 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
+ ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
}
}
+
+ /**
+ * Helper class to schedule the runnable if it hasn't scheduled on display thread inside window
+ * manager lock.
+ */
+ abstract static class LockedScheduler implements Runnable {
+ private final ActivityTaskManagerService mService;
+ private boolean mScheduled;
+
+ LockedScheduler(ActivityTaskManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public void run() {
+ synchronized (mService.mGlobalLock) {
+ mScheduled = false;
+ execute();
+ }
+ }
+
+ abstract void execute();
+
+ void scheduleIfNeeded() {
+ if (!mScheduled) {
+ mService.mH.post(this);
+ mScheduled = true;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 5f2113afa5b2..9ff99f5093d6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -157,33 +157,33 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
- return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
+ return mService.addWindow(this, window, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
outInsetsState, outActiveControls, UserHandle.getUserId(mUid));
}
@Override
- public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
- return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
+ return mService.addWindow(this, window, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
outInsetsState, outActiveControls, userId);
}
@Override
- public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
InsetsState outInsetsState) {
- return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
+ return mService.addWindow(this, window, attrs, viewVisibility, displayId,
new Rect() /* outFrame */, outContentInsets, outStableInsets,
new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
outInsetsState, mDummyControls, UserHandle.getUserId(mUid));
@@ -200,7 +200,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int relayout(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
@@ -209,7 +209,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
- int res = mService.relayoutWindow(this, window, seq, attrs,
+ int res = mService.relayoutWindow(this, window, attrs,
requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
outActiveControls, outSurfaceSize, outBLASTSurfaceControl);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ce602de702d6..9be4ace60642 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -464,9 +464,10 @@ class Task extends WindowContainer<WindowContainer> {
int mMinWidth;
int mMinHeight;
+ static final int LAYER_RANK_INVISIBLE = -1;
// Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
// This number will be assigned when we evaluate OOM scores for all visible tasks.
- int mLayerRank = -1;
+ int mLayerRank = LAYER_RANK_INVISIBLE;
/** Helper object used for updating override configuration. */
private Configuration mTmpConfig = new Configuration();
@@ -1539,7 +1540,6 @@ class Task extends WindowContainer<WindowContainer> {
if (isPersistable) {
mLastTimeMoved = System.currentTimeMillis();
}
- mRootWindowContainer.invalidateTaskLayers();
}
// Close up recents linked list.
@@ -7452,6 +7452,10 @@ class Task extends WindowContainer<WindowContainer> {
if (!mChildren.contains(child)) {
return;
}
+ if (child.asTask() != null) {
+ // Non-root task position changed.
+ mRootWindowContainer.invalidateTaskLayers();
+ }
final boolean isTop = getTopChild() == child;
if (isTop) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 76bd6ce380e9..55e6a784d331 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -441,6 +441,12 @@ final class TaskDisplayArea extends DisplayArea<Task> {
}
@Override
+ void onChildPositionChanged(WindowContainer child) {
+ super.onChildPositionChanged(child);
+ mRootWindowContainer.invalidateTaskLayers();
+ }
+
+ @Override
boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback,
boolean traverseTopToBottom) {
return callback.apply(this);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index cccda3a4f4b8..6904740343a6 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -241,7 +241,7 @@ class TaskSnapshotSurface implements StartingSurface {
mergeInsetsSources(insetsState, topFullscreenOpaqueWindow.getRequestedInsetsState());
}
try {
- final int res = session.addToDisplay(window, window.mSeq, layoutParams,
+ final int res = session.addToDisplay(window, layoutParams,
View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrames.frame,
tmpFrames.contentInsets, tmpFrames.stableInsets, tmpFrames.displayCutout,
null /* outInputChannel */, mTmpInsetsState, mTempControls);
@@ -258,7 +258,7 @@ class TaskSnapshotSurface implements StartingSurface {
insetsState);
window.setOuter(snapshotSurface);
try {
- session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
+ session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
mTempControls, sTmpSurfaceSize, sTmpSurfaceControl);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a7f5eca9af94..d434bf9d3816 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -411,7 +411,7 @@ public class WindowManagerService extends IWindowManager.Stub
* @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
*/
static boolean sDisableCustomTaskAnimationProperty =
- SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, false);
+ SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
"ro.sf.disable_triple_buffer";
@@ -864,8 +864,7 @@ public class WindowManagerService extends IWindowManager.Stub
void updateSystemUiSettings() {
boolean changed;
synchronized (mGlobalLock) {
- changed = ImmersiveModeConfirmation.loadSetting(mCurrentUserId, mContext)
- || PolicyControl.reloadFromSetting(mContext);
+ changed = ImmersiveModeConfirmation.loadSetting(mCurrentUserId, mContext);
}
if (changed) {
updateRotation(false /* alwaysSendConfiguration */, false /* forceRelayout */);
@@ -1374,9 +1373,8 @@ public class WindowManagerService extends IWindowManager.Stub
return false;
}
- public int addWindow(Session session, IWindow client, int seq,
- LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
- Rect outContentInsets, Rect outStableInsets,
+ public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
+ int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
int requestUserId) {
@@ -1557,7 +1555,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
final WindowState win = new WindowState(this, session, client, token, parentWindow,
- appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
+ appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
@@ -2099,7 +2097,7 @@ public class WindowManagerService extends IWindowManager.Stub
== PackageManager.PERMISSION_GRANTED;
}
- public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
+ public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
@@ -2141,17 +2139,15 @@ public class WindowManagerService extends IWindowManager.Stub
if (attrs != null) {
displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
win.mToken.adjustWindowParams(win, attrs);
- // if they don't have the permission, mask out the status bar bits
- if (seq == win.mSeq) {
- int systemUiVisibility = attrs.systemUiVisibility
- | attrs.subtreeSystemUiVisibility;
- if ((systemUiVisibility & DISABLE_MASK) != 0) {
- if (!hasStatusBarPermission(pid, uid)) {
- systemUiVisibility &= ~DISABLE_MASK;
- }
+ int systemUiVisibility = attrs.systemUiVisibility
+ | attrs.subtreeSystemUiVisibility;
+ if ((systemUiVisibility & DISABLE_MASK) != 0) {
+ // if they don't have the permission, mask out the status bar bits
+ if (!hasStatusBarPermission(pid, uid)) {
+ systemUiVisibility &= ~DISABLE_MASK;
}
- win.mSystemUiVisibility = systemUiVisibility;
}
+ win.mSystemUiVisibility = systemUiVisibility;
if (win.mAttrs.type != attrs.type) {
throw new IllegalArgumentException(
"Window type can not be changed after the window is added.");
@@ -5731,31 +5727,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void statusBarVisibilityChanged(int displayId, int visibility) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Caller does not hold permission "
- + android.Manifest.permission.STATUS_BAR);
- }
-
- synchronized (mGlobalLock) {
- final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
- if (displayContent != null) {
- displayContent.statusBarVisibilityChanged(visibility);
- } else {
- Slog.w(TAG, "statusBarVisibilityChanged with invalid displayId=" + displayId);
- }
- }
- }
-
- @Override
public void hideTransientBars(int displayId) {
mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
"hideTransientBars()");
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null) {
- displayContent.hideTransientBars();
+ displayContent.getInsetsPolicy().hideTransient();
} else {
Slog.w(TAG, "hideTransientBars with invalid displayId=" + displayId);
}
@@ -6112,7 +6090,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (inputMethodControlTarget != null) {
pw.print(" inputMethodControlTarget in display# "); pw.print(displayId);
- pw.print(' '); pw.println(inputMethodControlTarget.getWindow());
+ pw.print(' '); pw.println(inputMethodControlTarget);
}
});
pw.print(" mInTouchMode="); pw.println(mInTouchMode);
@@ -6164,7 +6142,6 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mRecentsAnimationController="); pw.println(mRecentsAnimationController);
mRecentsAnimationController.dump(pw, " ");
}
- PolicyControl.dump(" ", pw);
}
}
@@ -8270,4 +8247,14 @@ public class WindowManagerService extends IWindowManager.Stub
embeddedWindow.getName(), grantFocus);
}
}
+
+ @Override
+ public void holdLock(int durationMs) {
+ mContext.enforceCallingPermission(
+ Manifest.permission.INJECT_EVENTS, "holdLock requires shell identity");
+
+ synchronized (mGlobalLock) {
+ SystemClock.sleep(durationMs);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 268281b7a880..4b8a398582f3 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -221,6 +221,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
@Nullable
private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
+ /** The state for oom-adjustment calculation. */
+ private final OomScoreReferenceState mOomRefState;
+
public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info,
String name, int uid, int userId, Object owner, WindowProcessListener listener) {
mInfo = info;
@@ -232,6 +235,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mAtm = atm;
mDisplayId = INVALID_DISPLAY;
mBackgroundActivityStartCallback = mAtm.getBackgroundActivityStartCallback();
+ mOomRefState = new OomScoreReferenceState(this);
boolean isSysUiPackage = info.packageName.equals(
mAtm.getSysUiServiceComponentLocked().getPackageName());
@@ -688,15 +692,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
public boolean hasVisibleActivities() {
- synchronized (mAtm.mGlobalLockWithoutBoost) {
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = mActivities.get(i);
- if (r.mVisibleRequested) {
- return true;
- }
- }
- }
- return false;
+ return (mOomRefState.mActivityStateFlags & OomScoreReferenceState.FLAG_IS_VISIBLE) != 0;
}
@HotPath(caller = HotPath.LRU_UPDATE)
@@ -991,6 +987,34 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mHostActivities.remove(r);
}
+ private static class OomScoreReferenceState extends RootWindowContainer.LockedScheduler {
+ private static final int FLAG_IS_VISIBLE = 0x10000000;
+ private static final int FLAG_IS_PAUSING = 0x20000000;
+ private static final int FLAG_IS_STOPPING = 0x40000000;
+ private static final int FLAG_IS_STOPPING_FINISHING = 0x80000000;
+ /** @see Task#mLayerRank */
+ private static final int MASK_MIN_TASK_LAYER = 0x0000ffff;
+
+ private final WindowProcessController mOwner;
+ boolean mChanged;
+
+ /**
+ * The higher 16 bits are the activity states, and the lower 16 bits are the task layer
+ * rank. This field is written by window manager and read by activity manager.
+ */
+ volatile int mActivityStateFlags = MASK_MIN_TASK_LAYER;
+
+ OomScoreReferenceState(WindowProcessController owner) {
+ super(owner.mAtm);
+ mOwner = owner;
+ }
+
+ @Override
+ public void execute() {
+ mOwner.computeOomScoreReferenceStateIfNeeded();
+ }
+ }
+
public interface ComputeOomAdjCallback {
void onVisibleActivity();
void onPausedActivity();
@@ -998,64 +1022,102 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
void onOtherActivity();
}
+ /**
+ * Returns the minimum task layer rank. It should only be called if {@link #hasActivities}
+ * returns {@code true}.
+ */
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
- public int computeOomAdjFromActivities(int minTaskLayer, ComputeOomAdjCallback callback) {
+ public int computeOomAdjFromActivities(ComputeOomAdjCallback callback) {
+ final int flags = mOomRefState.mActivityStateFlags;
+ if ((flags & OomScoreReferenceState.FLAG_IS_VISIBLE) != 0) {
+ callback.onVisibleActivity();
+ } else if ((flags & OomScoreReferenceState.FLAG_IS_PAUSING) != 0) {
+ callback.onPausedActivity();
+ } else if ((flags & OomScoreReferenceState.FLAG_IS_STOPPING) != 0) {
+ callback.onStoppingActivity(
+ (flags & OomScoreReferenceState.FLAG_IS_STOPPING_FINISHING) != 0);
+ } else {
+ callback.onOtherActivity();
+ }
+ return flags & OomScoreReferenceState.MASK_MIN_TASK_LAYER;
+ }
+
+ void computeOomScoreReferenceStateIfNeeded() {
+ if (!mOomRefState.mChanged) {
+ return;
+ }
+ mOomRefState.mChanged = false;
+
// Since there could be more than one activities in a process record, we don't need to
// compute the OomAdj with each of them, just need to find out the activity with the
// "best" state, the order would be visible, pausing, stopping...
Task.ActivityState best = DESTROYED;
boolean finishing = true;
boolean visible = false;
- synchronized (mAtm.mGlobalLockWithoutBoost) {
- final int activitiesSize = mActivities.size();
- for (int j = 0; j < activitiesSize; j++) {
- final ActivityRecord r = mActivities.get(j);
- if (r.app != this) {
- Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
- + " instead of expected " + this);
- if (r.app == null || (r.app.mUid == mUid)) {
- // Only fix things up when they look sane
- r.setProcess(this);
- } else {
- continue;
- }
- }
- if (r.mVisibleRequested) {
- final Task task = r.getTask();
- if (task != null && minTaskLayer > 0) {
- final int layer = task.mLayerRank;
- if (layer >= 0 && minTaskLayer > layer) {
- minTaskLayer = layer;
- }
- }
- visible = true;
- // continue the loop, in case there are multiple visible activities in
- // this process, we'd find out the one with the minimal layer, thus it'll
- // get a higher adj score.
+ int minTaskLayer = Integer.MAX_VALUE;
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = mActivities.get(i);
+ if (r.app != this) {
+ Slog.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
+ + " instead of expected " + this);
+ if (r.app == null || (r.app.mUid == mUid)) {
+ // Only fix things up when they look valid.
+ r.setProcess(this);
} else {
- if (best != PAUSING && best != PAUSED) {
- if (r.isState(PAUSING, PAUSED)) {
- best = PAUSING;
- } else if (r.isState(STOPPING)) {
- best = STOPPING;
- // Not "finishing" if any of activity isn't finishing.
- finishing &= r.finishing;
- }
+ continue;
+ }
+ }
+ if (r.mVisibleRequested) {
+ final Task task = r.getTask();
+ if (task != null && minTaskLayer > 0) {
+ final int layer = task.mLayerRank;
+ if (layer >= 0 && minTaskLayer > layer) {
+ minTaskLayer = layer;
}
}
+ visible = true;
+ // continue the loop, in case there are multiple visible activities in
+ // this process, we'd find out the one with the minimal layer, thus it'll
+ // get a higher adj score.
+ } else if (best != PAUSING && best != PAUSED) {
+ if (r.isState(PAUSING, PAUSED)) {
+ best = PAUSING;
+ } else if (r.isState(STOPPING)) {
+ best = STOPPING;
+ // Not "finishing" if any of activity isn't finishing.
+ finishing &= r.finishing;
+ }
+ }
+
+ int stateFlags = minTaskLayer & OomScoreReferenceState.MASK_MIN_TASK_LAYER;
+ if (visible) {
+ stateFlags |= OomScoreReferenceState.FLAG_IS_VISIBLE;
+ } else if (best == PAUSING) {
+ stateFlags |= OomScoreReferenceState.FLAG_IS_PAUSING;
+ } else if (best == STOPPING) {
+ stateFlags |= OomScoreReferenceState.FLAG_IS_STOPPING;
+ if (finishing) {
+ stateFlags |= OomScoreReferenceState.FLAG_IS_STOPPING_FINISHING;
+ }
}
+ mOomRefState.mActivityStateFlags = stateFlags;
}
- if (visible) {
- callback.onVisibleActivity();
- } else if (best == PAUSING) {
- callback.onPausedActivity();
- } else if (best == STOPPING) {
- callback.onStoppingActivity(finishing);
- } else {
- callback.onOtherActivity();
+ }
+
+ void invalidateOomScoreReferenceState(boolean computeNow) {
+ mOomRefState.mChanged = true;
+ if (computeNow) {
+ computeOomScoreReferenceStateIfNeeded();
+ return;
}
+ mOomRefState.scheduleIfNeeded();
+ }
- return minTaskLayer;
+ /** Called when the process has some oom related changes and it is going to update oom-adj. */
+ private void prepareOomAdjustment() {
+ mAtm.mRootWindowContainer.rankTaskLayersIfNeeded();
+ // The task layer may not change but the activity state in the same task may change.
+ computeOomScoreReferenceStateIfNeeded();
}
public int computeRelaunchReason() {
@@ -1097,6 +1159,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
if (addPendingTopUid) {
mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
}
+ if (updateOomAdj) {
+ prepareOomAdjustment();
+ }
// Posting on handler so WM lock isn't held when we call into AM.
final Message m = PooledLambda.obtainMessage(WindowProcessListener::updateProcessInfo,
mListener, updateServiceConnectionActivities, activityChange, updateOomAdj);
@@ -1158,6 +1223,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
if (topProcessState == ActivityManager.PROCESS_STATE_TOP) {
mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
}
+ prepareOomAdjustment();
// Posting the message at the front of queue so WM lock isn't held when we call into AM,
// and the process state of starting activity can be updated quicker which will give it a
// higher scheduling group.
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 131e5bd67872..ad2817720387 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -302,7 +302,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final boolean mIsImWindow;
final boolean mIsWallpaper;
private final boolean mIsFloatingLayer;
- int mSeq;
int mViewVisibility;
int mSystemUiVisibility;
@@ -834,10 +833,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
- WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
- int viewVisibility, int ownerId, int showUserId,
- boolean ownerCanAddInternalSystemWindow) {
- this(service, s, c, token, parentWindow, appOp, seq, a, viewVisibility, ownerId, showUserId,
+ WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
+ int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow) {
+ this(service, s, c, token, parentWindow, appOp, a, viewVisibility, ownerId, showUserId,
ownerCanAddInternalSystemWindow, new PowerManagerWrapper() {
@Override
public void wakeUp(long time, @WakeReason int reason, String details) {
@@ -852,9 +850,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
- WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
- int viewVisibility, int ownerId, int showUserId,
- boolean ownerCanAddInternalSystemWindow, PowerManagerWrapper powerManagerWrapper) {
+ WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
+ int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
+ PowerManagerWrapper powerManagerWrapper) {
super(service);
mSession = s;
mClient = c;
@@ -871,7 +869,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mPolicy = mWmService.mPolicy;
mContext = mWmService.mContext;
DeathRecipient deathRecipient = new DeathRecipient();
- mSeq = seq;
mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
if (DEBUG) {
@@ -4029,7 +4026,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility)
+ " mHaveFrame=" + mHaveFrame
+ " mObscured=" + mObscured);
- pw.println(prefix + "mSeq=" + mSeq
+ pw.println(prefix
+ " mSystemUiVisibility=0x" + Integer.toHexString(mSystemUiVisibility));
}
if (!isVisibleByPolicy() || !mLegacyPolicyVisibilityAfterAnim || !mAppOpVisibility
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 44462c33ccdf..30f6fa68396e 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -133,6 +133,7 @@ cc_defaults {
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
"android.hardware.contexthub@1.0",
+ "android.hardware.gnss-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 17a05f3c7d1a..e39a3d1e9cb5 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -218,7 +218,7 @@ public:
void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj);
void setFocusedDisplay(JNIEnv* env, int32_t displayId);
void setInputDispatchMode(bool enabled, bool frozen);
- void setSystemUiVisibility(int32_t visibility);
+ void setSystemUiLightsOut(bool lightsOut);
void setPointerSpeed(int32_t speed);
void setInputDeviceEnabled(uint32_t deviceId, bool enabled);
void setShowTouches(bool enabled);
@@ -286,8 +286,8 @@ private:
// Display size information.
std::vector<DisplayViewport> viewports;
- // System UI visibility.
- int32_t systemUiVisibility;
+ // True if System UI is less noticeable.
+ bool systemUiLightsOut;
// Pointer speed.
int32_t pointerSpeed;
@@ -339,7 +339,7 @@ NativeInputManager::NativeInputManager(jobject contextObj,
{
AutoMutex _l(mLock);
- mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
+ mLocked.systemUiLightsOut = false;
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
@@ -366,8 +366,8 @@ void NativeInputManager::dump(std::string& dump) {
}
{
AutoMutex _l(mLock);
- dump += StringPrintf(INDENT "System UI Visibility: 0x%0" PRIx32 "\n",
- mLocked.systemUiVisibility);
+ dump += StringPrintf(INDENT "System UI Lights Out: %s\n",
+ toString(mLocked.systemUiLightsOut));
dump += StringPrintf(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed);
dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n",
toString(mLocked.pointerGesturesEnabled));
@@ -811,11 +811,11 @@ void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) {
mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen);
}
-void NativeInputManager::setSystemUiVisibility(int32_t visibility) {
+void NativeInputManager::setSystemUiLightsOut(bool lightsOut) {
AutoMutex _l(mLock);
- if (mLocked.systemUiVisibility != visibility) {
- mLocked.systemUiVisibility = visibility;
+ if (mLocked.systemUiLightsOut != lightsOut) {
+ mLocked.systemUiLightsOut = lightsOut;
updateInactivityTimeoutLocked();
}
}
@@ -826,9 +826,8 @@ void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
return;
}
- bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
- controller->setInactivityTimeout(lightsOut ? InactivityTimeout::SHORT
- : InactivityTimeout::NORMAL);
+ controller->setInactivityTimeout(mLocked.systemUiLightsOut ? InactivityTimeout::SHORT
+ : InactivityTimeout::NORMAL);
}
void NativeInputManager::setPointerSpeed(int32_t speed) {
@@ -1578,11 +1577,11 @@ static void nativeSetInputDispatchMode(JNIEnv* /* env */,
im->setInputDispatchMode(enabled, frozen);
}
-static void nativeSetSystemUiVisibility(JNIEnv* /* env */,
- jclass /* clazz */, jlong ptr, jint visibility) {
+static void nativeSetSystemUiLightsOut(JNIEnv* /* env */, jclass /* clazz */, jlong ptr,
+ jboolean lightsOut) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->setSystemUiVisibility(visibility);
+ im->setSystemUiLightsOut(lightsOut);
}
static jboolean nativeTransferTouchFocus(JNIEnv* env,
@@ -1802,7 +1801,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"nativeSetFocusedDisplay", "(JI)V", (void*)nativeSetFocusedDisplay},
{"nativeSetPointerCapture", "(JZ)V", (void*)nativeSetPointerCapture},
{"nativeSetInputDispatchMode", "(JZZ)V", (void*)nativeSetInputDispatchMode},
- {"nativeSetSystemUiVisibility", "(JI)V", (void*)nativeSetSystemUiVisibility},
+ {"nativeSetSystemUiLightsOut", "(JZ)V", (void*)nativeSetSystemUiLightsOut},
{"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)Z",
(void*)nativeTransferTouchFocus},
{"nativeSetPointerSpeed", "(JI)V", (void*)nativeSetPointerSpeed},
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 4e53aa218b71..91645ba1a9b9 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -30,9 +30,12 @@
#include <android/hardware/gnss/2.1/IGnssAntennaInfo.h>
#include <android/hardware/gnss/2.1/IGnssMeasurement.h>
#include <android/hardware/gnss/3.0/IGnssPsds.h>
+#include <android/hardware/gnss/BnGnss.h>
+#include <android/hardware/gnss/BnGnssPsdsCallback.h>
#include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
#include <android/hardware/gnss/measurement_corrections/1.1/IMeasurementCorrections.h>
#include <android/hardware/gnss/visibility_control/1.0/IGnssVisibilityControl.h>
+#include <binder/IServiceManager.h>
#include <nativehelper/JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
@@ -142,9 +145,10 @@ static JavaVM* sJvm;
using android::OK;
using android::sp;
-using android::wp;
using android::status_t;
using android::String16;
+using android::wp;
+using android::binder::Status;
using android::hardware::Return;
using android::hardware::Void;
@@ -152,6 +156,7 @@ using android::hardware::hidl_vec;
using android::hardware::hidl_string;
using android::hardware::hidl_death_recipient;
+using android::hardware::gnss::PsdsType;
using android::hardware::gnss::V1_0::GnssLocationFlags;
using android::hardware::gnss::V1_0::IAGnssRilCallback;
using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
@@ -161,11 +166,10 @@ using android::hardware::gnss::V1_0::IGnssNavigationMessageCallback;
using android::hardware::gnss::V1_0::IGnssNi;
using android::hardware::gnss::V1_0::IGnssNiCallback;
using android::hardware::gnss::V1_0::IGnssXtra;
+using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
using android::hardware::gnss::V3_0::IGnssPsds;
using android::hardware::gnss::V3_0::IGnssPsdsCallback;
-using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
-
using MeasurementCorrections_V1_0 = android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
using MeasurementCorrections_V1_1 = android::hardware::gnss::measurement_corrections::V1_1::MeasurementCorrections;
@@ -224,6 +228,10 @@ using android::hardware::gnss::measurement_corrections::V1_0::GnssSingleSatCorre
using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControlCallback;
+using IGnssAidl = android::hardware::gnss::IGnss;
+using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
+using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback;
+
struct GnssDeathRecipient : virtual public hidl_death_recipient
{
// hidl_death_recipient interface
@@ -245,7 +253,9 @@ sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
sp<IGnss_V2_1> gnssHal_V2_1 = nullptr;
sp<IGnss_V3_0> gnssHal_V3_0 = nullptr;
+sp<IGnssAidl> gnssHalAidl = nullptr;
sp<IGnssPsds> gnssPsdsIface = nullptr;
+sp<IGnssPsdsAidl> gnssPsdsAidlIface = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IAGnssRil_V1_0> agnssRilIface = nullptr;
sp<IAGnssRil_V2_0> agnssRilIface_V2_0 = nullptr;
@@ -445,6 +455,19 @@ static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodNa
}
}
+static jboolean checkAidlStatus(const Status& status, const char* errorMessage,
+ const bool success) {
+ if (!status.isOk()) {
+ ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str());
+ return JNI_FALSE;
+ }
+ if (!success) {
+ ALOGE("AIDL return failure: %s", errorMessage);
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) {
jobject version = env->NewObject(class_gnssConfiguration_halInterfaceVersion,
method_halInterfaceVersionCtor, major, minor);
@@ -928,20 +951,31 @@ Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback_V2_0::GnssSys
* GnssPsdsCallback class implements the callback methods for the IGnssPsds
* interface.
*/
+struct GnssPsdsCallbackAidl : public android::hardware::gnss::BnGnssPsdsCallback {
+ Status downloadRequestCb(PsdsType psdsType) override {
+ ALOGD("%s. psdsType: %d", __func__, static_cast<int32_t>(psdsType));
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, psdsType);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+ }
+};
+
+/*
+ * GnssPsdsCallback class implements the callback methods for the IGnssPsds
+ * interface.
+ */
struct GnssPsdsCallback : public IGnssPsdsCallback {
Return<void> downloadRequestCb() override;
Return<void> downloadRequestCb_3_0(int32_t psdsType) override;
};
Return<void> GnssPsdsCallback::downloadRequestCb() {
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, /* psdsType= */ 1);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
+ return downloadRequestCb_3_0(/* psdsType= */ 1);
}
Return<void> GnssPsdsCallback::downloadRequestCb_3_0(int32_t psdsType) {
- ALOGD("%s: %d", __func__, psdsType);
+ ALOGD("%s. psdsType: %d", __func__, psdsType);
JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, psdsType);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -1917,6 +1951,11 @@ struct GnssBatchingCallback_V2_0 : public IGnssBatchingCallback_V2_0 {
/* Initializes the GNSS service handle. */
static void android_location_GnssLocationProvider_set_gps_service_handle() {
+ gnssHalAidl = waitForVintfService<IGnssAidl>();
+ if (gnssHalAidl != nullptr) {
+ ALOGD("Successfully got GNSS AIDL handle.");
+ }
+
ALOGD("Trying IGnss_V3_0::getService()");
gnssHal_V3_0 = IGnss_V3_0::getService();
if (gnssHal_V3_0 != nullptr) {
@@ -2168,7 +2207,15 @@ static void android_location_GnssNative_init_once(JNIEnv* env, jobject obj,
ALOGD("Link to death notification successful");
}
- if (gnssHal_V3_0 != nullptr) {
+ if (gnssHalAidl != nullptr) {
+ sp<IGnssPsdsAidl> gnssPsdsAidl;
+ auto status = gnssHalAidl->getExtensionPsds(&gnssPsdsAidl);
+ if (status.isOk()) {
+ gnssPsdsAidlIface = gnssPsdsAidl;
+ } else {
+ ALOGD("Unable to get a handle to PSDS AIDL interface.");
+ }
+ } else if (gnssHal_V3_0 != nullptr) {
auto gnssPsds = gnssHal_V3_0->getExtensionPsds();
if (!gnssPsds.isOk()) {
ALOGD("Unable to get a handle to Psds");
@@ -2470,19 +2517,28 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* /* env */, jo
}
// Set IGnssPsds or IGnssXtra callback.
- sp<IGnssPsdsCallback> gnssPsdsCbIface = new GnssPsdsCallback();
- if (gnssPsdsIface != nullptr) {
- result = gnssPsdsIface->setCallback_3_0(gnssPsdsCbIface);
- if (!checkHidlReturn(result, "IGnssPsds setCallback() failed.")) {
- gnssPsdsIface = nullptr;
- }
- } else if (gnssXtraIface != nullptr) {
- result = gnssXtraIface->setCallback(gnssPsdsCbIface);
- if (!checkHidlReturn(result, "IGnssXtra setCallback() failed.")) {
- gnssXtraIface = nullptr;
+ if (gnssPsdsAidlIface != nullptr) {
+ sp<IGnssPsdsCallbackAidl> gnssPsdsCallbackAidl = new GnssPsdsCallbackAidl();
+ bool success;
+ auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl, &success);
+ if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.", success)) {
+ gnssPsdsAidlIface = nullptr;
}
} else {
- ALOGI("Unable to initialize IGnssPsds/IGnssXtra interface.");
+ sp<IGnssPsdsCallback> gnssPsdsCbIface = new GnssPsdsCallback();
+ if (gnssPsdsIface != nullptr) {
+ result = gnssPsdsIface->setCallback_3_0(gnssPsdsCbIface);
+ if (!checkHidlReturn(result, "IGnssPsds setCallback() failed.")) {
+ gnssPsdsIface = nullptr;
+ }
+ } else if (gnssXtraIface != nullptr) {
+ result = gnssXtraIface->setCallback(gnssPsdsCbIface);
+ if (!checkHidlReturn(result, "IGnssXtra setCallback() failed.")) {
+ gnssXtraIface = nullptr;
+ }
+ } else {
+ ALOGI("Unable to initialize IGnssPsds/IGnssXtra interface.");
+ }
}
// Set IAGnss.hal callback.
@@ -2743,19 +2799,29 @@ static void android_location_GnssLocationProvider_inject_location(JNIEnv* /* env
static jboolean android_location_GnssLocationProvider_supports_psds(
JNIEnv* /* env */, jobject /* obj */) {
- return (gnssPsdsIface != nullptr || gnssXtraIface != nullptr) ? JNI_TRUE : JNI_FALSE;
+ return (gnssPsdsAidlIface != nullptr || gnssPsdsIface != nullptr || gnssXtraIface != nullptr)
+ ? JNI_TRUE
+ : JNI_FALSE;
}
static void android_location_GnssLocationProvider_inject_psds_data(JNIEnv* env, jobject /* obj */,
jbyteArray data, jint length,
jint psdsType) {
- if (gnssPsdsIface == nullptr && gnssXtraIface == nullptr) {
+ if (gnssPsdsAidlIface == nullptr && gnssPsdsIface == nullptr && gnssXtraIface == nullptr) {
ALOGE("%s: IGnssPsds or IGnssXtra interface not available.", __func__);
return;
}
jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0));
- if (gnssPsdsIface != nullptr) {
+ if (gnssPsdsAidlIface != nullptr) {
+ bool success;
+ auto status = gnssPsdsAidlIface->injectPsdsData(static_cast<PsdsType>(psdsType),
+ std::vector<uint8_t>((const uint8_t*)bytes,
+ (const uint8_t*)bytes +
+ length),
+ &success);
+ checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.", success);
+ } else if (gnssPsdsIface != nullptr) {
auto result = gnssPsdsIface->injectPsdsData_3_0(psdsType,
std::string((const char*)bytes, length));
checkHidlReturn(result, "IGnssPsds injectPsdsData() failed.");
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index e8bf468f032e..bbcb3122c9bb 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1468,7 +1468,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_
// Need a shared pointer: will be passing it into all unpacking jobs.
std::shared_ptr<ZipArchive> zipFile(zipFileHandle, [](ZipArchiveHandle h) { CloseArchive(h); });
void* cookie = nullptr;
- const auto libFilePrefix = path::join(constants().libDir, abi);
+ const auto libFilePrefix = path::join(constants().libDir, abi) + "/";
if (StartIteration(zipFile.get(), &cookie, libFilePrefix, constants().libSuffix)) {
LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath;
return false;
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 33317a38853e..8b1e9c52ec58 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -40,7 +40,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemService;
import com.android.server.people.data.DataManager;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -95,28 +94,57 @@ public class PeopleService extends SystemService {
* @throws SecurityException if the caller is not system or root
*/
private static void enforceSystemOrRoot(String message) {
- int uid = Binder.getCallingUid();
- if (!UserHandle.isSameApp(uid, Process.SYSTEM_UID) && uid != Process.ROOT_UID) {
+ if (!isSystemOrRoot()) {
throw new SecurityException("Only system may " + message);
}
}
+ private static boolean isSystemOrRoot() {
+ final int uid = Binder.getCallingUid();
+ return UserHandle.isSameApp(uid, Process.SYSTEM_UID) || uid == Process.ROOT_UID;
+ }
+
+
+ /**
+ * Enforces that only the system, root UID or SystemUI can make certain calls.
+ *
+ * @param message used as message if SecurityException is thrown
+ * @throws SecurityException if the caller is not system or root
+ */
+ private static void enforceSystemRootOrSystemUI(Context context, String message) {
+ if (isSystemOrRoot()) return;
+ context.enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+ message);
+ }
+
private final class BinderService extends IPeopleManager.Stub {
@Override
public ParceledListSlice<ConversationChannel> getRecentConversations() {
enforceSystemOrRoot("get recent conversations");
- return new ParceledListSlice<>(new ArrayList<>());
+ return new ParceledListSlice<>(
+ mDataManager.getRecentConversations(
+ Binder.getCallingUserHandle().getIdentifier()));
}
@Override
public void removeRecentConversation(String packageName, int userId, String shortcutId) {
enforceSystemOrRoot("remove a recent conversation");
+ mDataManager.removeRecentConversation(packageName, userId, shortcutId,
+ Binder.getCallingUserHandle().getIdentifier());
}
@Override
public void removeAllRecentConversations() {
enforceSystemOrRoot("remove all recent conversations");
+ mDataManager.removeAllRecentConversations(
+ Binder.getCallingUserHandle().getIdentifier());
+ }
+
+ @Override
+ public long getLastInteraction(String packageName, int userId, String shortcutId) {
+ enforceSystemRootOrSystemUI(getContext(), "get last interaction");
+ return mDataManager.getLastInteraction(packageName, userId, shortcutId);
}
}
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index 17378285276f..45f389cbd3ff 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -90,6 +90,11 @@ public class ConversationInfo {
@Nullable
private String mNotificationChannelId;
+ @Nullable
+ private String mParentNotificationChannelId;
+
+ private long mLastEventTimestamp;
+
@ShortcutFlags
private int mShortcutFlags;
@@ -102,6 +107,8 @@ public class ConversationInfo {
mContactUri = builder.mContactUri;
mContactPhoneNumber = builder.mContactPhoneNumber;
mNotificationChannelId = builder.mNotificationChannelId;
+ mParentNotificationChannelId = builder.mParentNotificationChannelId;
+ mLastEventTimestamp = builder.mLastEventTimestamp;
mShortcutFlags = builder.mShortcutFlags;
mConversationFlags = builder.mConversationFlags;
}
@@ -129,14 +136,32 @@ public class ConversationInfo {
}
/**
- * ID of the {@link android.app.NotificationChannel} where the notifications for this
- * conversation are posted.
+ * ID of the conversation-specific {@link android.app.NotificationChannel} where the
+ * notifications for this conversation are posted.
*/
@Nullable
String getNotificationChannelId() {
return mNotificationChannelId;
}
+ /**
+ * ID of the parent {@link android.app.NotificationChannel} for this conversation. This is the
+ * notification channel where the notifications are posted before this conversation is
+ * customized by the user.
+ */
+ @Nullable
+ String getParentNotificationChannelId() {
+ return mParentNotificationChannelId;
+ }
+
+ /**
+ * Timestamp of the last event, {@code 0L} if there are no events. This timestamp is for
+ * identifying and sorting the recent conversations. It may only count a subset of event types.
+ */
+ long getLastEventTimestamp() {
+ return mLastEventTimestamp;
+ }
+
/** Whether the shortcut for this conversation is set long-lived by the app. */
public boolean isShortcutLongLived() {
return hasShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED);
@@ -202,6 +227,8 @@ public class ConversationInfo {
&& Objects.equals(mContactUri, other.mContactUri)
&& Objects.equals(mContactPhoneNumber, other.mContactPhoneNumber)
&& Objects.equals(mNotificationChannelId, other.mNotificationChannelId)
+ && Objects.equals(mParentNotificationChannelId, other.mParentNotificationChannelId)
+ && Objects.equals(mLastEventTimestamp, other.mLastEventTimestamp)
&& mShortcutFlags == other.mShortcutFlags
&& mConversationFlags == other.mConversationFlags;
}
@@ -209,7 +236,8 @@ public class ConversationInfo {
@Override
public int hashCode() {
return Objects.hash(mShortcutId, mLocusId, mContactUri, mContactPhoneNumber,
- mNotificationChannelId, mShortcutFlags, mConversationFlags);
+ mNotificationChannelId, mParentNotificationChannelId, mLastEventTimestamp,
+ mShortcutFlags, mConversationFlags);
}
@Override
@@ -221,6 +249,8 @@ public class ConversationInfo {
sb.append(", contactUri=").append(mContactUri);
sb.append(", phoneNumber=").append(mContactPhoneNumber);
sb.append(", notificationChannelId=").append(mNotificationChannelId);
+ sb.append(", parentNotificationChannelId=").append(mParentNotificationChannelId);
+ sb.append(", lastEventTimestamp=").append(mLastEventTimestamp);
sb.append(", shortcutFlags=0x").append(Integer.toHexString(mShortcutFlags));
sb.append(" [");
if (isShortcutLongLived()) {
@@ -280,6 +310,11 @@ public class ConversationInfo {
protoOutputStream.write(ConversationInfoProto.NOTIFICATION_CHANNEL_ID,
mNotificationChannelId);
}
+ if (mParentNotificationChannelId != null) {
+ protoOutputStream.write(ConversationInfoProto.PARENT_NOTIFICATION_CHANNEL_ID,
+ mParentNotificationChannelId);
+ }
+ protoOutputStream.write(ConversationInfoProto.LAST_EVENT_TIMESTAMP, mLastEventTimestamp);
protoOutputStream.write(ConversationInfoProto.SHORTCUT_FLAGS, mShortcutFlags);
protoOutputStream.write(ConversationInfoProto.CONVERSATION_FLAGS, mConversationFlags);
if (mContactPhoneNumber != null) {
@@ -300,6 +335,8 @@ public class ConversationInfo {
out.writeInt(mShortcutFlags);
out.writeInt(mConversationFlags);
out.writeUTF(mContactPhoneNumber != null ? mContactPhoneNumber : "");
+ out.writeUTF(mParentNotificationChannelId != null ? mParentNotificationChannelId : "");
+ out.writeLong(mLastEventTimestamp);
} catch (IOException e) {
Slog.e(TAG, "Failed to write fields to backup payload.", e);
return null;
@@ -338,6 +375,14 @@ public class ConversationInfo {
builder.setNotificationChannelId(protoInputStream.readString(
ConversationInfoProto.NOTIFICATION_CHANNEL_ID));
break;
+ case (int) ConversationInfoProto.PARENT_NOTIFICATION_CHANNEL_ID:
+ builder.setParentNotificationChannelId(protoInputStream.readString(
+ ConversationInfoProto.PARENT_NOTIFICATION_CHANNEL_ID));
+ break;
+ case (int) ConversationInfoProto.LAST_EVENT_TIMESTAMP:
+ builder.setLastEventTimestamp(protoInputStream.readLong(
+ ConversationInfoProto.LAST_EVENT_TIMESTAMP));
+ break;
case (int) ConversationInfoProto.SHORTCUT_FLAGS:
builder.setShortcutFlags(protoInputStream.readInt(
ConversationInfoProto.SHORTCUT_FLAGS));
@@ -382,6 +427,11 @@ public class ConversationInfo {
if (!TextUtils.isEmpty(contactPhoneNumber)) {
builder.setContactPhoneNumber(contactPhoneNumber);
}
+ String parentNotificationChannelId = in.readUTF();
+ if (!TextUtils.isEmpty(parentNotificationChannelId)) {
+ builder.setParentNotificationChannelId(parentNotificationChannelId);
+ }
+ builder.setLastEventTimestamp(in.readLong());
} catch (IOException e) {
Slog.e(TAG, "Failed to read conversation info fields from backup payload.", e);
return null;
@@ -408,6 +458,11 @@ public class ConversationInfo {
@Nullable
private String mNotificationChannelId;
+ @Nullable
+ private String mParentNotificationChannelId;
+
+ private long mLastEventTimestamp;
+
@ShortcutFlags
private int mShortcutFlags;
@@ -427,6 +482,8 @@ public class ConversationInfo {
mContactUri = conversationInfo.mContactUri;
mContactPhoneNumber = conversationInfo.mContactPhoneNumber;
mNotificationChannelId = conversationInfo.mNotificationChannelId;
+ mParentNotificationChannelId = conversationInfo.mParentNotificationChannelId;
+ mLastEventTimestamp = conversationInfo.mLastEventTimestamp;
mShortcutFlags = conversationInfo.mShortcutFlags;
mConversationFlags = conversationInfo.mConversationFlags;
}
@@ -456,6 +513,16 @@ public class ConversationInfo {
return this;
}
+ Builder setParentNotificationChannelId(String parentNotificationChannelId) {
+ mParentNotificationChannelId = parentNotificationChannelId;
+ return this;
+ }
+
+ Builder setLastEventTimestamp(long lastEventTimestamp) {
+ mLastEventTimestamp = lastEventTimestamp;
+ return this;
+ }
+
Builder setShortcutFlags(@ShortcutFlags int shortcutFlags) {
mShortcutFlags = shortcutFlags;
return this;
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 4d1129567ca6..87f2c581ef8f 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -24,6 +24,7 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
+import android.app.people.ConversationChannel;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.usage.UsageEvents;
@@ -74,8 +75,11 @@ import com.android.server.notification.ShortcutHelper;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.PriorityQueue;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -96,6 +100,7 @@ public class DataManager {
private static final long QUERY_EVENTS_MAX_AGE_MS = 5L * DateUtils.MINUTE_IN_MILLIS;
private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L;
+ @VisibleForTesting static final int MAX_CACHED_RECENT_SHORTCUTS = 30;
private final Context mContext;
private final Injector mInjector;
@@ -208,6 +213,83 @@ public class DataManager {
mContext.getPackageName(), intentFilter, callingUserId);
}
+ /** Returns the cached non-customized recent conversations. */
+ public List<ConversationChannel> getRecentConversations(@UserIdInt int callingUserId) {
+ List<ConversationChannel> conversationChannels = new ArrayList<>();
+ forPackagesInProfile(callingUserId, packageData -> {
+ String packageName = packageData.getPackageName();
+ int userId = packageData.getUserId();
+ packageData.forAllConversations(conversationInfo -> {
+ if (!isCachedRecentConversation(conversationInfo)) {
+ return;
+ }
+ String shortcutId = conversationInfo.getShortcutId();
+ ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
+ int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
+ NotificationChannel parentChannel =
+ mNotificationManagerInternal.getNotificationChannel(packageName, uid,
+ conversationInfo.getParentNotificationChannelId());
+ if (shortcutInfo == null || parentChannel == null) {
+ return;
+ }
+ conversationChannels.add(
+ new ConversationChannel(shortcutInfo, parentChannel,
+ conversationInfo.getLastEventTimestamp(),
+ hasActiveNotifications(packageName, userId, shortcutId)));
+ });
+ });
+ return conversationChannels;
+ }
+
+ /**
+ * Uncaches the shortcut that's associated with the specified conversation so this conversation
+ * will not show up in the recent conversations list.
+ */
+ public void removeRecentConversation(String packageName, int userId, String shortcutId,
+ @UserIdInt int callingUserId) {
+ if (!hasActiveNotifications(packageName, userId, shortcutId)) {
+ mShortcutServiceInternal.uncacheShortcuts(callingUserId, mContext.getPackageName(),
+ packageName, Collections.singletonList(shortcutId), userId,
+ ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ }
+ }
+
+ /**
+ * Uncaches the shortcuts for all the recent conversations that they don't have active
+ * notifications.
+ */
+ public void removeAllRecentConversations(@UserIdInt int callingUserId) {
+ forPackagesInProfile(callingUserId, packageData -> {
+ String packageName = packageData.getPackageName();
+ int userId = packageData.getUserId();
+ List<String> idsToUncache = new ArrayList<>();
+ packageData.forAllConversations(conversationInfo -> {
+ String shortcutId = conversationInfo.getShortcutId();
+ if (isCachedRecentConversation(conversationInfo)
+ && !hasActiveNotifications(packageName, userId, shortcutId)) {
+ idsToUncache.add(shortcutId);
+ }
+ });
+ mShortcutServiceInternal.uncacheShortcuts(callingUserId, mContext.getPackageName(),
+ packageName, idsToUncache, userId, ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ });
+ }
+
+ /**
+ * Returns the last notification interaction with the specified conversation. If the
+ * conversation can't be found or no interactions have been recorded, returns 0L.
+ */
+ public long getLastInteraction(String packageName, int userId, String shortcutId) {
+ final PackageData packageData = getPackage(packageName, userId);
+ if (packageData != null) {
+ final ConversationInfo conversationInfo = packageData.getConversationInfo(shortcutId);
+ if (conversationInfo != null) {
+ return conversationInfo.getLastEventTimestamp();
+ }
+ }
+ return 0L;
+ }
+
/** Reports the sharing related {@link AppTargetEvent} from App Prediction Manager. */
public void reportShareTargetEvent(@NonNull AppTargetEvent event,
@NonNull IntentFilter intentFilter) {
@@ -277,7 +359,6 @@ public class DataManager {
}
pruneUninstalledPackageData(userData);
- final NotificationListener notificationListener = mNotificationListeners.get(userId);
userData.forAllPackages(packageData -> {
if (signal.isCanceled()) {
return;
@@ -290,20 +371,7 @@ public class DataManager {
packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_SMS);
}
packageData.pruneOrphanEvents();
- if (notificationListener != null) {
- String packageName = packageData.getPackageName();
- packageData.forAllConversations(conversationInfo -> {
- if (conversationInfo.isShortcutCachedForNotification()
- && conversationInfo.getNotificationChannelId() == null
- && !notificationListener.hasActiveNotifications(
- packageName, conversationInfo.getShortcutId())) {
- mShortcutServiceInternal.uncacheShortcuts(userId,
- mContext.getPackageName(), packageName,
- Collections.singletonList(conversationInfo.getShortcutId()),
- userId, ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- }
- });
- }
+ cleanupCachedShortcuts(userId, MAX_CACHED_RECENT_SHORTCUTS);
});
}
@@ -466,7 +534,8 @@ public class DataManager {
@NonNull String packageName, @UserIdInt int userId,
@Nullable List<String> shortcutIds) {
@ShortcutQuery.QueryFlags int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC
- | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
+ | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
+ | ShortcutQuery.FLAG_MATCH_CACHED;
return mShortcutServiceInternal.getShortcuts(
UserHandle.USER_SYSTEM, mContext.getPackageName(),
/*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null,
@@ -526,6 +595,68 @@ public class DataManager {
return packageData;
}
+ private boolean isCachedRecentConversation(ConversationInfo conversationInfo) {
+ return conversationInfo.isShortcutCachedForNotification()
+ && conversationInfo.getNotificationChannelId() == null
+ && conversationInfo.getParentNotificationChannelId() != null
+ && conversationInfo.getLastEventTimestamp() > 0L;
+ }
+
+ private boolean hasActiveNotifications(String packageName, @UserIdInt int userId,
+ String shortcutId) {
+ NotificationListener notificationListener = mNotificationListeners.get(userId);
+ return notificationListener != null
+ && notificationListener.hasActiveNotifications(packageName, shortcutId);
+ }
+
+ /**
+ * Cleans up the oldest cached shortcuts that don't have active notifications for the recent
+ * conversations. After the cleanup, normally, the total number of cached shortcuts will be
+ * less than or equal to the target count. However, there are exception cases: e.g. when all
+ * the existing cached shortcuts have active notifications.
+ */
+ private void cleanupCachedShortcuts(@UserIdInt int userId, int targetCachedCount) {
+ UserData userData = getUnlockedUserData(userId);
+ if (userData == null) {
+ return;
+ }
+ // pair of <package name, conversation info>
+ List<Pair<String, ConversationInfo>> cachedConvos = new ArrayList<>();
+ userData.forAllPackages(packageData ->
+ packageData.forAllConversations(conversationInfo -> {
+ if (isCachedRecentConversation(conversationInfo)) {
+ cachedConvos.add(
+ Pair.create(packageData.getPackageName(), conversationInfo));
+ }
+ })
+ );
+ if (cachedConvos.size() <= targetCachedCount) {
+ return;
+ }
+ int numToUncache = cachedConvos.size() - targetCachedCount;
+ // Max heap keeps the oldest cached conversations.
+ PriorityQueue<Pair<String, ConversationInfo>> maxHeap = new PriorityQueue<>(
+ numToUncache + 1,
+ Comparator.comparingLong((Pair<String, ConversationInfo> pair) ->
+ pair.second.getLastEventTimestamp()).reversed());
+ for (Pair<String, ConversationInfo> cached : cachedConvos) {
+ if (hasActiveNotifications(cached.first, userId, cached.second.getShortcutId())) {
+ continue;
+ }
+ maxHeap.offer(cached);
+ if (maxHeap.size() > numToUncache) {
+ maxHeap.poll();
+ }
+ }
+ while (!maxHeap.isEmpty()) {
+ Pair<String, ConversationInfo> toUncache = maxHeap.poll();
+ mShortcutServiceInternal.uncacheShortcuts(userId,
+ mContext.getPackageName(), toUncache.first,
+ Collections.singletonList(toUncache.second.getShortcutId()),
+ userId, ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ }
+ }
+
@VisibleForTesting
@WorkerThread
void addOrUpdateConversationInfo(@NonNull ShortcutInfo shortcutInfo) {
@@ -736,9 +867,21 @@ public class DataManager {
public void onShortcutsAddedOrUpdated(@NonNull String packageName,
@NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
mInjector.getBackgroundExecutor().execute(() -> {
+ PackageData packageData = getPackage(packageName, user.getIdentifier());
for (ShortcutInfo shortcut : shortcuts) {
if (ShortcutHelper.isConversationShortcut(
shortcut, mShortcutServiceInternal, user.getIdentifier())) {
+ if (shortcut.isCached()) {
+ ConversationInfo conversationInfo = packageData != null
+ ? packageData.getConversationInfo(shortcut.getId()) : null;
+ if (conversationInfo == null
+ || !conversationInfo.isShortcutCachedForNotification()) {
+ // This is a newly cached shortcut. Clean up the existing cached
+ // shortcuts to ensure the cache size is under the limit.
+ cleanupCachedShortcuts(user.getIdentifier(),
+ MAX_CACHED_RECENT_SHORTCUTS - 1);
+ }
+ }
addOrUpdateConversationInfo(shortcut);
}
}
@@ -757,14 +900,16 @@ public class DataManager {
Slog.e(TAG, "Package not found: " + packageName, e);
}
PackageData packageData = getPackage(packageName, user.getIdentifier());
+ Set<String> shortcutIds = new HashSet<>();
for (ShortcutInfo shortcutInfo : shortcuts) {
if (packageData != null) {
packageData.deleteDataForConversation(shortcutInfo.getId());
}
- if (uid != Process.INVALID_UID) {
- mNotificationManagerInternal.onConversationRemoved(
- shortcutInfo.getPackage(), uid, shortcutInfo.getId());
- }
+ shortcutIds.add(shortcutInfo.getId());
+ }
+ if (uid != Process.INVALID_UID) {
+ mNotificationManagerInternal.onConversationRemoved(
+ packageName, uid, shortcutIds);
}
});
}
@@ -797,6 +942,16 @@ public class DataManager {
});
if (packageData != null) {
+ ConversationInfo conversationInfo = packageData.getConversationInfo(shortcutId);
+ if (conversationInfo == null) {
+ return;
+ }
+ ConversationInfo updated = new ConversationInfo.Builder(conversationInfo)
+ .setLastEventTimestamp(sbn.getPostTime())
+ .setParentNotificationChannelId(sbn.getNotification().getChannelId())
+ .build();
+ packageData.getConversationStore().addOrUpdate(updated);
+
EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
eventHistory.addEvent(new Event(sbn.getPostTime(), Event.TYPE_NOTIFICATION_POSTED));
@@ -817,16 +972,7 @@ public class DataManager {
int count = mActiveNotifCounts.getOrDefault(conversationKey, 0) - 1;
if (count <= 0) {
mActiveNotifCounts.remove(conversationKey);
- // The shortcut was cached by Notification Manager synchronously when the
- // associated notification was posted. Uncache it here when all the
- // associated notifications are removed.
- if (conversationInfo.isShortcutCachedForNotification()
- && conversationInfo.getNotificationChannelId() == null) {
- mShortcutServiceInternal.uncacheShortcuts(mUserId,
- mContext.getPackageName(), sbn.getPackageName(),
- Collections.singletonList(conversationInfo.getShortcutId()),
- mUserId, ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- }
+ cleanupCachedShortcuts(mUserId, MAX_CACHED_RECENT_SHORTCUTS);
} else {
mActiveNotifCounts.put(conversationKey, count);
}
@@ -882,24 +1028,6 @@ public class DataManager {
conversationStore.addOrUpdate(builder.build());
}
- synchronized void cleanupCachedShortcuts() {
- for (Pair<String, String> conversationKey : mActiveNotifCounts.keySet()) {
- String packageName = conversationKey.first;
- String shortcutId = conversationKey.second;
- PackageData packageData = getPackage(packageName, mUserId);
- ConversationInfo conversationInfo =
- packageData != null ? packageData.getConversationInfo(shortcutId) : null;
- if (conversationInfo != null
- && conversationInfo.isShortcutCachedForNotification()
- && conversationInfo.getNotificationChannelId() == null) {
- mShortcutServiceInternal.uncacheShortcuts(mUserId,
- mContext.getPackageName(), packageName,
- Collections.singletonList(shortcutId),
- mUserId, ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- }
- }
- }
-
synchronized boolean hasActiveNotifications(String packageName, String shortcutId) {
return mActiveNotifCounts.containsKey(Pair.create(packageName, shortcutId));
}
@@ -972,16 +1100,7 @@ public class DataManager {
@Override
public void onReceive(Context context, Intent intent) {
- forAllUnlockedUsers(userData -> {
- NotificationListener listener = mNotificationListeners.get(userData.getUserId());
- // Clean up the cached shortcuts because all the notifications are cleared after
- // system shutdown. The associated shortcuts need to be uncached to keep in sync
- // unless the settings are changed by the user.
- if (listener != null) {
- listener.cleanupCachedShortcuts();
- }
- userData.forAllPackages(PackageData::saveToDisk);
- });
+ forAllUnlockedUsers(userData -> userData.forAllPackages(PackageData::saveToDisk));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 5d8f662301c1..a250c217614d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -351,7 +351,7 @@ public class MockingOomAdjusterTests {
doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).hasActivities();
- doAnswer(answer((minTaskLayer, callback) -> {
+ doAnswer(answer(callback -> {
Field field = callback.getClass().getDeclaredField("adj");
field.set(callback, VISIBLE_APP_ADJ);
field = callback.getClass().getDeclaredField("foregroundActivities");
@@ -361,7 +361,7 @@ public class MockingOomAdjusterTests {
field = callback.getClass().getDeclaredField("schedGroup");
field.set(callback, SCHED_GROUP_TOP_APP);
return 0;
- })).when(wpc).computeOomAdjFromActivities(anyInt(),
+ })).when(wpc).computeOomAdjFromActivities(
any(WindowProcessController.ComputeOomAdjCallback.class));
sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 90e1cfcd305a..79936ce6d623 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -77,6 +77,7 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<uses-permission android:name="android.permission.DUMP"/>
<uses-permission android:name="android.permission.READ_DREAM_STATE"/>
+ <uses-permission android:name="android.permission.READ_DREAM_SUPPRESSION"/>
<uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index b7355ce92c28..b9c2e56a4d90 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -49,6 +49,7 @@ import androidx.test.InstrumentationRegistry;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
+import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -94,6 +95,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
@Mock private IBinder mMockBinder;
@Mock private IAccessibilityServiceClient mMockServiceClient;
@Mock private WindowMagnificationManager mMockWindowMagnificationMgr;
+ @Mock private MagnificationController mMockMagnificationController;
private AccessibilityUserState mUserState;
private MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
@@ -110,6 +112,8 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
LocalServices.addService(
ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
+ when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
+ mMockWindowMagnificationMgr);
mA11yms = new AccessibilityManagerService(
InstrumentationRegistry.getContext(),
mMockPackageManager,
@@ -117,7 +121,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
mMockSystemActionPerformer,
mMockA11yWindowManager,
mMockA11yDisplayListener,
- mMockWindowMagnificationMgr);
+ mMockMagnificationController);
final AccessibilityUserState userState = new AccessibilityUserState(
mA11yms.getCurrentUserIdLocked(), mMockContext, mA11yms);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index cd8e39cfd2e7..3e34f8a428db 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -62,7 +62,7 @@ import org.mockito.MockitoAnnotations;
* Tests for MagnificationController.
*/
@RunWith(AndroidJUnit4.class)
-public class MagnificationTransitionControllerTest {
+public class MagnificationControllerTest {
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
private static final Region MAGNIFICATION_REGION = new Region(0, 0, 500, 600);
@@ -75,7 +75,7 @@ public class MagnificationTransitionControllerTest {
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@Mock private AccessibilityManagerService mService;
- @Mock private MagnificationTransitionController.TransitionCallBack mTransitionCallBack;
+ @Mock private MagnificationController.TransitionCallBack mTransitionCallBack;
@Mock private Context mContext;
@Mock private FullScreenMagnificationController mScreenMagnificationController;
@Captor private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor;
@@ -83,7 +83,7 @@ public class MagnificationTransitionControllerTest {
private MockWindowMagnificationConnection mMockConnection;
private WindowMagnificationManager mWindowMagnificationManager;
private MockContentResolver mMockResolver;
- private MagnificationTransitionController mMagnificationTransitionController;
+ private MagnificationController mMagnificationController;
@Before
public void setUp() throws Exception {
@@ -95,14 +95,12 @@ public class MagnificationTransitionControllerTest {
Settings.Secure.putFloatForUser(mMockResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, DEFAULT_SCALE,
CURRENT_USER_ID);
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID);
- when(mService.getFullScreenMagnificationController()).thenReturn(
- mScreenMagnificationController);
- when(mService.getWindowMagnificationMgr()).thenReturn(mWindowMagnificationManager);
+ mWindowMagnificationManager = Mockito.spy(
+ new WindowMagnificationManager(mContext, CURRENT_USER_ID));
mMockConnection = new MockWindowMagnificationConnection(true);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mMagnificationTransitionController = new MagnificationTransitionController(mService,
- new Object());
+ mMagnificationController = new MagnificationController(mService, new Object(), mContext,
+ mScreenMagnificationController, mWindowMagnificationManager);
}
@After
@@ -113,7 +111,7 @@ public class MagnificationTransitionControllerTest {
@Test
public void transitionToWindowMode_notMagnifying_doNothing() throws RemoteException {
setMagnificationModeSettings(MODE_FULLSCREEN);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
@@ -130,7 +128,7 @@ public class MagnificationTransitionControllerTest {
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
@@ -147,11 +145,11 @@ public class MagnificationTransitionControllerTest {
public void transitionToWindowMode_disablingWindowMode_enablingWindowWithFormerCenter()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
@@ -166,7 +164,7 @@ public class MagnificationTransitionControllerTest {
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
mMockConnection.invokeCallbacks();
@@ -186,7 +184,7 @@ public class MagnificationTransitionControllerTest {
magnificationBounds.bottom + 100);
setMagnificationEnabled(MODE_WINDOW, magnifiedCenter.x, magnifiedCenter.y);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
mMockConnection.invokeCallbacks();
@@ -202,11 +200,11 @@ public class MagnificationTransitionControllerTest {
public void transitionToFullScreenMode_disablingFullScreen_enableFullScreenWithFormerCenter()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
@@ -221,7 +219,7 @@ public class MagnificationTransitionControllerTest {
public void interruptDuringTransitionToFullScreenMode_windowMagnifying_notifyTransitionFailed()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
@@ -237,7 +235,23 @@ public class MagnificationTransitionControllerTest {
verify(mTransitionCallBack).onResult(false);
}
+ @Test
+ public void onDisplayRemoved_notifyAllModules() {
+ mMagnificationController.onDisplayRemoved(TEST_DISPLAY);
+
+ verify(mScreenMagnificationController).onDisplayRemoved(TEST_DISPLAY);
+ verify(mWindowMagnificationManager).onDisplayRemoved(TEST_DISPLAY);
+ }
+
+ @Test
+ public void updateUserIdIfNeeded_AllModulesAvailable_setUserId() {
+ mMagnificationController.updateUserIdIfNeeded(CURRENT_USER_ID);
+
+ verify(mScreenMagnificationController).setUserId(CURRENT_USER_ID);
+ verify(mWindowMagnificationManager).setUserId(CURRENT_USER_ID);
+ }
private void setMagnificationEnabled(int mode) throws RemoteException {
+
setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 89b0a03a25bb..d5be3ede40d2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -369,6 +369,16 @@ public class WindowMagnificationManagerTest {
assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
}
+ @Test
+ public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
+ mWindowMagnificationManager.requestConnection(true);
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+
+ mWindowMagnificationManager.onDisplayRemoved(TEST_DISPLAY);
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
final int len = pointersLocation.length;
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 73dda0736d2f..da25fd612463 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -309,7 +309,8 @@ public class DisplayManagerServiceTest {
zeroRect, new Rect(0, 0, 10, 10), zeroRect, zeroRect);
displayDeviceInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY;
displayDevice.setDisplayDeviceInfo(displayDeviceInfo);
- displayManager.handleDisplayDeviceAdded(displayDevice);
+ displayManager.getDisplayDeviceRepository()
+ .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
// Find the display id of the added FakeDisplayDevice
DisplayManagerService.BinderService bs = displayManager.new BinderService();
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index 301a9fe64d5e..b312e52ff74b 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -28,8 +28,6 @@ import android.view.SurfaceControl;
import org.junit.Before;
import org.junit.Test;
-import java.util.ArrayList;
-
public class LogicalDisplayTest {
private static final int DISPLAY_ID = 0;
private static final int LAYER_STACK = 0;
@@ -51,9 +49,16 @@ public class LogicalDisplayTest {
mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice);
when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(displayDeviceInfo);
- ArrayList<DisplayDevice> displayDevices = new ArrayList<>();
- displayDevices.add(mDisplayDevice);
- mLogicalDisplay.updateLocked(displayDevices);
+ DisplayDeviceRepository repo = new DisplayDeviceRepository(
+ new DisplayManagerService.SyncRoot(), new DisplayDeviceRepository.Listener() {
+ @Override
+ public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {}
+
+ @Override
+ public void onTraversalRequested() {}
+ });
+ repo.onDisplayDeviceEvent(mDisplayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
+ mLogicalDisplay.updateLocked(repo);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 08f558e6d99c..1385376b740d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -54,8 +54,6 @@ public class ArcInitiationActionFromAvrTest {
private Context mContextSpy;
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
- private HdmiCecController mHdmiCecController;
- private HdmiControlService mHdmiControlService;
private FakeNativeWrapper mNativeWrapper;
private ArcInitiationActionFromAvr mAction;
@@ -78,7 +76,7 @@ public class ArcInitiationActionFromAvrTest {
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService =
+ HdmiControlService hdmiControlService =
new HdmiControlService(mContextSpy) {
@Override
boolean isPowerStandby() {
@@ -110,7 +108,7 @@ public class ArcInitiationActionFromAvrTest {
}
};
- mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) {
+ mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
protected void setPreferredAddress(int addr) {
}
@@ -118,18 +116,18 @@ public class ArcInitiationActionFromAvrTest {
mHdmiCecLocalDeviceAudioSystem.init();
Looper looper = mTestLooper.getLooper();
- mHdmiControlService.setIoLooper(looper);
+ hdmiControlService.setIoLooper(looper);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
- mHdmiControlService.setCecController(mHdmiCecController);
- mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
- mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
- mHdmiControlService.initPortInfo();
+ HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
+ hdmiControlService, mNativeWrapper, hdmiControlService.getAtomWriter());
+ hdmiControlService.setCecController(hdmiCecController);
+ hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
+ hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
+ hdmiControlService.initPortInfo();
mAction = new ArcInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
- mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ hdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
}
@@ -142,7 +140,7 @@ public class ArcInitiationActionFromAvrTest {
assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
- mHdmiControlService.sendCecCommand(
+ mNativeWrapper.onCecMessage(
HdmiCecMessageBuilder.buildReportArcInitiated(
Constants.ADDR_TV,
Constants.ADDR_AUDIO_SYSTEM));
@@ -174,7 +172,7 @@ public class ArcInitiationActionFromAvrTest {
assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
- mHdmiControlService.handleCecCommand(HdmiCecMessageBuilder.buildReportArcTerminated(
+ mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportArcTerminated(
Constants.ADDR_TV,
Constants.ADDR_AUDIO_SYSTEM));
mTestLooper.dispatchAll();
@@ -192,7 +190,7 @@ public class ArcInitiationActionFromAvrTest {
assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
- mHdmiControlService.handleCecCommand(
+ mNativeWrapper.onCecMessage(
HdmiCecMessageBuilder.buildFeatureAbortCommand(
Constants.ADDR_TV,
Constants.ADDR_AUDIO_SYSTEM, Constants.MESSAGE_INITIATE_ARC,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 4afbbf741102..169f885a7253 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -56,8 +56,6 @@ public class ArcTerminationActionFromAvrTest {
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
private ArcTerminationActionFromAvr mAction;
- private HdmiCecController mHdmiCecController;
- private HdmiControlService mHdmiControlService;
private FakeNativeWrapper mNativeWrapper;
private TestLooper mTestLooper = new TestLooper();
@@ -79,7 +77,7 @@ public class ArcTerminationActionFromAvrTest {
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService =
+ HdmiControlService hdmiControlService =
new HdmiControlService(mContextSpy) {
@Override
void wakeUp() {
@@ -112,16 +110,16 @@ public class ArcTerminationActionFromAvrTest {
};
Looper looper = mTestLooper.getLooper();
- mHdmiControlService.setIoLooper(looper);
+ hdmiControlService.setIoLooper(looper);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
- mHdmiControlService.setCecController(mHdmiCecController);
- mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
- mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
- mHdmiControlService.initPortInfo();
-
- mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) {
+ HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
+ hdmiControlService, mNativeWrapper, hdmiControlService.getAtomWriter());
+ hdmiControlService.setCecController(hdmiCecController);
+ hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
+ hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
+ hdmiControlService.initPortInfo();
+
+ mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
protected void setPreferredAddress(int addr) {
}
@@ -130,7 +128,7 @@ public class ArcTerminationActionFromAvrTest {
mAction = new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
- mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ hdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
mTestLooper.dispatchAll();
}
@@ -173,7 +171,7 @@ public class ArcTerminationActionFromAvrTest {
HdmiCecMessage arcTerminatedResponse = HdmiCecMessageBuilder.buildReportArcTerminated(
Constants.ADDR_TV, Constants.ADDR_AUDIO_SYSTEM);
- mHdmiControlService.handleCecCommand(arcTerminatedResponse);
+ mNativeWrapper.onCecMessage(arcTerminatedResponse);
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 01f0a3d398df..2c42791fabce 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -16,7 +16,11 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.tv.cec.V1_0.CecMessage;
+import android.hardware.tv.cec.V1_0.HotplugEvent;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.RemoteException;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.HdmiCecController.NativeWrapper;
@@ -29,6 +33,8 @@ import java.util.List;
/** Fake {@link NativeWrapper} useful for testing. */
final class FakeNativeWrapper implements NativeWrapper {
+ private static final String TAG = "FakeNativeWrapper";
+
private final int[] mPollAddressResponse =
new int[] {
SendMessageResult.NACK,
@@ -52,6 +58,7 @@ final class FakeNativeWrapper implements NativeWrapper {
private final HashMap<Integer, Integer> mMessageSendResult = new HashMap<>();
private int mMyPhysicalAddress = 0;
private HdmiPortInfo[] mHdmiPortInfo = null;
+ private HdmiCecController.HdmiCecCallback mCallback = null;
@Override
public String nativeInit() {
@@ -59,7 +66,9 @@ final class FakeNativeWrapper implements NativeWrapper {
}
@Override
- public void setCallback(HdmiCecController.HdmiCecCallback callback) {}
+ public void setCallback(HdmiCecController.HdmiCecCallback callback) {
+ this.mCallback = callback;
+ }
@Override
public int nativeSendCecCommand(
@@ -119,6 +128,42 @@ final class FakeNativeWrapper implements NativeWrapper {
return false;
}
+ public void onCecMessage(HdmiCecMessage hdmiCecMessage) {
+ if (mCallback == null) {
+ return;
+ }
+ CecMessage message = new CecMessage();
+ message.initiator = hdmiCecMessage.getSource();
+ message.destination = hdmiCecMessage.getDestination();
+ ArrayList<Byte> body = new ArrayList<>();
+ body.add((byte) hdmiCecMessage.getOpcode());
+ for (byte param : hdmiCecMessage.getParams()) {
+ body.add(param);
+ }
+ message.body = body;
+ try {
+ mCallback.onCecMessage(message);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending CEC message", e);
+ }
+ }
+
+ public void onHotplugEvent(int port, boolean connected) {
+ if (mCallback == null) {
+ return;
+ }
+
+ HotplugEvent hotplugEvent = new HotplugEvent();
+ hotplugEvent.portId = port;
+ hotplugEvent.connected = connected;
+
+ try {
+ mCallback.onHotplugEvent(hotplugEvent);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending hotplug event", e);
+ }
+ }
+
public List<HdmiCecMessage> getResultMessages() {
return new ArrayList<>(mResultMessages);
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index c60d5fb95846..6e7ec2a88140 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -70,7 +70,6 @@ public class HdmiCecControllerTest {
}
}
- private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private int mLogicalAddress = 16;
private AllocateAddressCallback mCallback =
@@ -87,10 +86,11 @@ public class HdmiCecControllerTest {
public void SetUp() {
mMyLooper = mTestLooper.getLooper();
mMyLooper = mTestLooper.getLooper();
- mHdmiControlService = new MyHdmiControlService(InstrumentationRegistry.getTargetContext());
+ HdmiControlService hdmiControlService = new MyHdmiControlService(
+ InstrumentationRegistry.getTargetContext());
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+ hdmiControlService, mNativeWrapper, hdmiControlService.getAtomWriter());
}
/** Tests for {@link HdmiCecController#allocateLogicalAddress} */
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index d160a3fab186..498ebf4a2ef9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -39,7 +39,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -370,16 +369,11 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mStandby).isFalse();
}
- // Playback device does not handle routing control related feature right now
- @Ignore("b/120845532")
@Test
- public void handleSetStreamPath_underCurrentDevice() {
- assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(0);
+ public void handleSetStreamPath() {
HdmiCecMessage message =
HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2100);
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)).isTrue();
- // TODO(amyjojo): Move set and get LocalActivePath to Control Service.
- assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(1);
}
@Test
@@ -786,8 +780,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void handleSetStreamPath_afterHotplug_broadcastsActiveSource() {
- mHdmiControlService.onHotplug(1, false);
- mHdmiControlService.onHotplug(1, true);
+ mNativeWrapper.onHotplugEvent(1, false);
+ mNativeWrapper.onHotplugEvent(1, true);
HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
mPlaybackPhysicalAddress);
@@ -803,8 +797,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void handleSetStreamPath_afterHotplug_hasCorrectActiveSource() {
- mHdmiControlService.onHotplug(1, false);
- mHdmiControlService.onHotplug(1, true);
+ mNativeWrapper.onHotplugEvent(1, false);
+ mNativeWrapper.onHotplugEvent(1, true);
HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
mPlaybackPhysicalAddress);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
index c5d94875b684..c6823ebfd655 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
@@ -37,6 +37,7 @@ public final class ConversationInfoTest {
private static final Uri CONTACT_URI = Uri.parse("tel:+1234567890");
private static final String PHONE_NUMBER = "+1234567890";
private static final String NOTIFICATION_CHANNEL_ID = "test : abc";
+ private static final String PARENT_NOTIFICATION_CHANNEL_ID = "test";
@Test
public void testBuild() {
@@ -46,6 +47,8 @@ public final class ConversationInfoTest {
.setContactUri(CONTACT_URI)
.setContactPhoneNumber(PHONE_NUMBER)
.setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
+ .setParentNotificationChannelId(PARENT_NOTIFICATION_CHANNEL_ID)
+ .setLastEventTimestamp(100L)
.setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED
| ShortcutInfo.FLAG_CACHED_NOTIFICATIONS)
.setImportant(true)
@@ -62,6 +65,9 @@ public final class ConversationInfoTest {
assertEquals(CONTACT_URI, conversationInfo.getContactUri());
assertEquals(PHONE_NUMBER, conversationInfo.getContactPhoneNumber());
assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
+ assertEquals(PARENT_NOTIFICATION_CHANNEL_ID,
+ conversationInfo.getParentNotificationChannelId());
+ assertEquals(100L, conversationInfo.getLastEventTimestamp());
assertTrue(conversationInfo.isShortcutLongLived());
assertTrue(conversationInfo.isShortcutCachedForNotification());
assertTrue(conversationInfo.isImportant());
@@ -84,6 +90,8 @@ public final class ConversationInfoTest {
assertNull(conversationInfo.getContactUri());
assertNull(conversationInfo.getContactPhoneNumber());
assertNull(conversationInfo.getNotificationChannelId());
+ assertNull(conversationInfo.getParentNotificationChannelId());
+ assertEquals(0L, conversationInfo.getLastEventTimestamp());
assertFalse(conversationInfo.isShortcutLongLived());
assertFalse(conversationInfo.isShortcutCachedForNotification());
assertFalse(conversationInfo.isImportant());
@@ -103,6 +111,8 @@ public final class ConversationInfoTest {
.setContactUri(CONTACT_URI)
.setContactPhoneNumber(PHONE_NUMBER)
.setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
+ .setParentNotificationChannelId(PARENT_NOTIFICATION_CHANNEL_ID)
+ .setLastEventTimestamp(100L)
.setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
.setImportant(true)
.setNotificationSilenced(true)
@@ -122,6 +132,8 @@ public final class ConversationInfoTest {
assertEquals(CONTACT_URI, destination.getContactUri());
assertEquals(PHONE_NUMBER, destination.getContactPhoneNumber());
assertEquals(NOTIFICATION_CHANNEL_ID, destination.getNotificationChannelId());
+ assertEquals(PARENT_NOTIFICATION_CHANNEL_ID, destination.getParentNotificationChannelId());
+ assertEquals(100L, destination.getLastEventTimestamp());
assertTrue(destination.isShortcutLongLived());
assertFalse(destination.isImportant());
assertTrue(destination.isNotificationSilenced());
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index b2f7abbf84df..f37054d269b1 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -29,6 +29,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
@@ -45,6 +46,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
import android.app.job.JobScheduler;
+import android.app.people.ConversationChannel;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
@@ -112,6 +114,7 @@ public final class DataManagerTest {
private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123";
private static final String PHONE_NUMBER = "+1234567890";
private static final String NOTIFICATION_CHANNEL_ID = "test : sc";
+ private static final String PARENT_NOTIFICATION_CHANNEL_ID = "test";
private static final long MILLIS_PER_MINUTE = 1000L * 60L;
@Mock private Context mContext;
@@ -133,10 +136,12 @@ public final class DataManagerTest {
private ScheduledExecutorService mExecutorService;
private NotificationChannel mNotificationChannel;
+ private NotificationChannel mParentNotificationChannel;
private DataManager mDataManager;
private CancellationSignal mCancellationSignal;
private ShortcutChangeCallback mShortcutChangeCallback;
private BroadcastReceiver mShutdownBroadcastReceiver;
+ private ShortcutInfo mShortcutInfo;
private TestInjector mInjector;
@Before
@@ -157,6 +162,11 @@ public final class DataManagerTest {
}).when(mPackageManagerInternal).forEachInstalledPackage(any(Consumer.class), anyInt());
addLocalServiceMock(NotificationManagerInternal.class, mNotificationManagerInternal);
+ mParentNotificationChannel = new NotificationChannel(
+ PARENT_NOTIFICATION_CHANNEL_ID, "test channel",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ when(mNotificationManagerInternal.getNotificationChannel(anyString(), anyInt(),
+ anyString())).thenReturn(mParentNotificationChannel);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
@@ -199,6 +209,7 @@ public final class DataManagerTest {
when(mStatusBarNotification.getUser()).thenReturn(UserHandle.of(USER_ID_PRIMARY));
when(mStatusBarNotification.getPostTime()).thenReturn(System.currentTimeMillis());
when(mNotification.getShortcutId()).thenReturn(TEST_SHORTCUT_ID);
+ when(mNotification.getChannelId()).thenReturn(PARENT_NOTIFICATION_CHANNEL_ID);
mNotificationChannel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "test channel", NotificationManager.IMPORTANCE_DEFAULT);
@@ -212,6 +223,13 @@ public final class DataManagerTest {
when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
anyString(), anyInt(), any())).thenReturn(true);
+
+ mShortcutInfo = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ when(mShortcutServiceInternal.getShortcuts(
+ anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
+ anyInt(), anyInt(), anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(mShortcutInfo));
verify(mShortcutServiceInternal).addShortcutChangeCallback(
mShortcutChangeCallbackCaptor.capture());
mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue();
@@ -417,29 +435,28 @@ public final class DataManagerTest {
List<Range<Long>> activeNotificationOpenTimeSlots = getActiveSlotsForTestShortcut(
Event.NOTIFICATION_EVENT_TYPES);
assertEquals(1, activeNotificationOpenTimeSlots.size());
- verify(mShortcutServiceInternal).uncacheShortcuts(
- anyInt(), any(), eq(TEST_PKG_NAME),
- eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY),
- eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
@Test
- public void testNotificationDismissed() {
+ public void testUncacheShortcutsWhenNotificationsDismissed() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
-
- ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
- buildPerson());
- mDataManager.addOrUpdateConversationInfo(shortcut);
-
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
- // Post one notification.
- shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- mDataManager.addOrUpdateConversationInfo(shortcut);
- listenerService.onNotificationPosted(mStatusBarNotification);
+ // The cached conversations are above the limit because every conversation has active
+ // notifications. To uncache one of them, the notifications for that conversation need to
+ // be dismissed.
+ for (int i = 0; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
+ String shortcutId = TEST_SHORTCUT_ID + i;
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ when(mNotification.getShortcutId()).thenReturn(shortcutId);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ }
- // Post another notification.
+ // Post another notification for the last conversation.
listenerService.onNotificationPosted(mStatusBarNotification);
// Removing one of the two notifications does not un-cache the shortcut.
@@ -452,13 +469,12 @@ public final class DataManagerTest {
listenerService.onNotificationRemoved(mStatusBarNotification, null,
NotificationListenerService.REASON_CANCEL_ALL);
verify(mShortcutServiceInternal).uncacheShortcuts(
- anyInt(), any(), eq(TEST_PKG_NAME),
- eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY),
+ anyInt(), any(), eq(TEST_PKG_NAME), anyList(), eq(USER_ID_PRIMARY),
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
@Test
- public void testShortcutNotUncachedIfNotificationChannelCreated() {
+ public void testConversationIsNotRecentIfCustomized() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
@@ -472,15 +488,12 @@ public final class DataManagerTest {
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
+ assertEquals(1, mDataManager.getRecentConversations(USER_ID_PRIMARY).size());
+
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
- listenerService.onNotificationRemoved(mStatusBarNotification, null,
- NotificationListenerService.REASON_CANCEL_ALL);
- verify(mShortcutServiceInternal, never()).uncacheShortcuts(
- anyInt(), any(), eq(TEST_PKG_NAME),
- eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY),
- eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ assertTrue(mDataManager.getRecentConversations(USER_ID_PRIMARY).isEmpty());
}
@Test
@@ -561,53 +574,6 @@ public final class DataManagerTest {
}
@Test
- public void testUncacheShortcutWhenShutdown() {
- mDataManager.onUserUnlocked(USER_ID_PRIMARY);
-
- ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
- buildPerson());
- mDataManager.addOrUpdateConversationInfo(shortcut);
-
- NotificationListenerService listenerService =
- mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
-
- listenerService.onNotificationPosted(mStatusBarNotification);
- shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- mDataManager.addOrUpdateConversationInfo(shortcut);
-
- mShutdownBroadcastReceiver.onReceive(mContext, new Intent());
- verify(mShortcutServiceInternal).uncacheShortcuts(
- anyInt(), any(), eq(TEST_PKG_NAME),
- eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY),
- eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
- }
-
- @Test
- public void testDoNotUncacheShortcutWhenShutdownIfNotificationChannelCreated() {
- mDataManager.onUserUnlocked(USER_ID_PRIMARY);
-
- ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
- buildPerson());
- mDataManager.addOrUpdateConversationInfo(shortcut);
-
- NotificationListenerService listenerService =
- mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
-
- listenerService.onNotificationPosted(mStatusBarNotification);
- shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- mDataManager.addOrUpdateConversationInfo(shortcut);
-
- listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
- mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
-
- mShutdownBroadcastReceiver.onReceive(mContext, new Intent());
- verify(mShortcutServiceInternal, never()).uncacheShortcuts(
- anyInt(), any(), eq(TEST_PKG_NAME),
- eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY),
- eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
- }
-
- @Test
public void testShortcutAddedOrUpdated() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
@@ -623,17 +589,19 @@ public final class DataManagerTest {
}
@Test
- public void testShortcutDeleted() {
+ public void testShortcutsDeleted() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut1 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc1",
buildPerson());
ShortcutInfo shortcut2 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc2",
buildPerson());
+ ShortcutInfo shortcut3 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc3",
+ buildPerson());
mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME,
- Arrays.asList(shortcut1, shortcut2), UserHandle.of(USER_ID_PRIMARY));
+ Arrays.asList(shortcut1, shortcut2, shortcut3), UserHandle.of(USER_ID_PRIMARY));
mShortcutChangeCallback.onShortcutsRemoved(TEST_PKG_NAME,
- Collections.singletonList(shortcut1), UserHandle.of(USER_ID_PRIMARY));
+ List.of(shortcut1, shortcut3), UserHandle.of(USER_ID_PRIMARY));
List<ConversationInfo> conversations = getConversationsInPrimary();
@@ -641,7 +609,7 @@ public final class DataManagerTest {
assertEquals("sc2", conversations.get(0).getShortcutId());
verify(mNotificationManagerInternal)
- .onConversationRemoved(TEST_PKG_NAME, TEST_PKG_UID, "sc1");
+ .onConversationRemoved(TEST_PKG_NAME, TEST_PKG_UID, Set.of("sc1", "sc3"));
}
@Test
@@ -767,20 +735,57 @@ public final class DataManagerTest {
}
@Test
- public void testPruneInactiveCachedShortcuts() {
+ public void testDoNotUncacheShortcutWithActiveNotifications() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
- ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
- buildPerson());
- shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- mDataManager.addOrUpdateConversationInfo(shortcut);
+ for (int i = 0; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
+ String shortcutId = TEST_SHORTCUT_ID + i;
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ when(mNotification.getShortcutId()).thenReturn(shortcutId);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ }
mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+ verify(mShortcutServiceInternal, never()).uncacheShortcuts(
+ anyInt(), anyString(), anyString(), anyList(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void testUncacheOldestCachedShortcut() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+
+ for (int i = 0; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
+ String shortcutId = TEST_SHORTCUT_ID + i;
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ when(mNotification.getShortcutId()).thenReturn(shortcutId);
+ when(mStatusBarNotification.getPostTime()).thenReturn(100L + i);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ listenerService.onNotificationRemoved(mStatusBarNotification, null,
+ NotificationListenerService.REASON_CANCEL);
+ }
+
+ // Only the shortcut #0 is uncached, all the others are not.
verify(mShortcutServiceInternal).uncacheShortcuts(
anyInt(), any(), eq(TEST_PKG_NAME),
- eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY),
+ eq(Collections.singletonList(TEST_SHORTCUT_ID + 0)), eq(USER_ID_PRIMARY),
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ for (int i = 1; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
+ verify(mShortcutServiceInternal, never()).uncacheShortcuts(
+ anyInt(), anyString(), anyString(),
+ eq(Collections.singletonList(TEST_SHORTCUT_ID + i)), anyInt(),
+ eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ }
}
@Test
@@ -810,6 +815,148 @@ public final class DataManagerTest {
assertEquals(conversationInfo.getShortcutId(), TEST_SHORTCUT_ID);
}
+ @Test
+ public void testGetRecentConversations() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+
+ List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY);
+ assertEquals(1, result.size());
+ assertEquals(shortcut.getId(), result.get(0).getShortcutInfo().getId());
+ assertEquals(mParentNotificationChannel.getId(),
+ result.get(0).getParentNotificationChannel().getId());
+ assertEquals(mStatusBarNotification.getPostTime(), result.get(0).getLastEventTimestamp());
+ assertTrue(result.get(0).hasActiveNotifications());
+ }
+
+ @Test
+ public void testGetLastInteraction() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+
+ assertEquals(mStatusBarNotification.getPostTime(),
+ mDataManager.getLastInteraction(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID));
+ assertEquals(0L,
+ mDataManager.getLastInteraction("not_test_pkg", USER_ID_PRIMARY, TEST_SHORTCUT_ID));
+ assertEquals(0L,
+ mDataManager.getLastInteraction(TEST_PKG_NAME, USER_ID_PRIMARY_MANAGED,
+ TEST_SHORTCUT_ID));
+ assertEquals(0L,
+ mDataManager.getLastInteraction(TEST_PKG_NAME, USER_ID_SECONDARY,
+ TEST_SHORTCUT_ID));
+ }
+
+ @Test
+ public void testNonCachedShortcutNotInRecentList() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY_MANAGED,
+ TEST_SHORTCUT_ID, buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+
+ List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ public void testCustomizedConversationNotInRecentList() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ // Post a notification and customize the notification settings.
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+ mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+ List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ public void testRemoveRecentConversation() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ listenerService.onNotificationRemoved(mStatusBarNotification, null,
+ NotificationListenerService.REASON_CANCEL);
+ mDataManager.removeRecentConversation(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ USER_ID_PRIMARY);
+
+ verify(mShortcutServiceInternal).uncacheShortcuts(
+ anyInt(), any(), eq(TEST_PKG_NAME), eq(Collections.singletonList(TEST_SHORTCUT_ID)),
+ eq(USER_ID_PRIMARY), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ }
+
+ @Test
+ public void testRemoveAllRecentConversations() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut1 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "1",
+ buildPerson());
+ shortcut1.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut1);
+
+ ShortcutInfo shortcut2 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "2",
+ buildPerson());
+ shortcut2.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut2);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+
+ // Post a notification and then dismiss it for conversation #1.
+ when(mNotification.getShortcutId()).thenReturn("1");
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ listenerService.onNotificationRemoved(mStatusBarNotification, null,
+ NotificationListenerService.REASON_CANCEL);
+
+ // Post a notification for conversation #2, but don't dismiss it. Its shortcut won't be
+ // uncached when removeAllRecentConversations() is called.
+ when(mNotification.getShortcutId()).thenReturn("2");
+ listenerService.onNotificationPosted(mStatusBarNotification);
+
+ mDataManager.removeAllRecentConversations(USER_ID_PRIMARY);
+
+ verify(mShortcutServiceInternal).uncacheShortcuts(
+ anyInt(), any(), eq(TEST_PKG_NAME), eq(Collections.singletonList("1")),
+ eq(USER_ID_PRIMARY), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ verify(mShortcutServiceInternal, never()).uncacheShortcuts(
+ anyInt(), any(), eq(TEST_PKG_NAME), eq(Collections.singletonList("2")),
+ eq(USER_ID_PRIMARY), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ }
+
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
LocalServices.removeServiceForTest(clazz);
LocalServices.addService(clazz, mock);
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 419fb14df340..6febae00f0fb 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -80,6 +80,7 @@ import com.android.server.SystemService;
import com.android.server.lights.LightsManager;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.power.PowerManagerService.BatteryReceiver;
+import com.android.server.power.PowerManagerService.BinderService;
import com.android.server.power.PowerManagerService.Injector;
import com.android.server.power.PowerManagerService.NativeWrapper;
import com.android.server.power.PowerManagerService.UserSwitchedReceiver;
@@ -179,6 +180,7 @@ public class PowerManagerServiceTest {
when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true);
when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
+ when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
mDisplayPowerRequest = new DisplayPowerRequest();
addLocalServiceMock(LightsManager.class, mLightsManagerMock);
@@ -983,6 +985,74 @@ public class PowerManagerServiceTest {
}
@Test
+ public void testIsAmbientDisplaySuppressedForTokenByApp_ambientDisplayUnavailable()
+ throws Exception {
+ createService();
+ when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(false);
+
+ BinderService service = mService.getBinderServiceInstance();
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test", Binder.getCallingUid()))
+ .isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForTokenByApp_default()
+ throws Exception {
+ createService();
+
+ BinderService service = mService.getBinderServiceInstance();
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test", Binder.getCallingUid()))
+ .isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForTokenByApp_suppressedByCallingApp()
+ throws Exception {
+ createService();
+ BinderService service = mService.getBinderServiceInstance();
+ service.suppressAmbientDisplay("test", true);
+
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test", Binder.getCallingUid()))
+ .isTrue();
+ // Check that isAmbientDisplaySuppressedForTokenByApp doesn't return true for another app.
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test", /* appUid= */ 123))
+ .isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForTokenByApp_notSuppressedByCallingApp()
+ throws Exception {
+ createService();
+ BinderService service = mService.getBinderServiceInstance();
+ service.suppressAmbientDisplay("test", false);
+
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test", Binder.getCallingUid()))
+ .isFalse();
+ // Check that isAmbientDisplaySuppressedForTokenByApp doesn't return true for another app.
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test", /* appUid= */ 123))
+ .isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForTokenByApp_multipleTokensSuppressedByCallingApp()
+ throws Exception {
+ createService();
+ BinderService service = mService.getBinderServiceInstance();
+ service.suppressAmbientDisplay("test1", true);
+ service.suppressAmbientDisplay("test2", true);
+
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test1", Binder.getCallingUid()))
+ .isTrue();
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test2", Binder.getCallingUid()))
+ .isTrue();
+ // Check that isAmbientDisplaySuppressedForTokenByApp doesn't return true for another app.
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test1", /* appUid= */ 123))
+ .isFalse();
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test2", /* appUid= */ 123))
+ .isFalse();
+ }
+
+ @Test
public void testSetPowerBoost_redirectsCallToNativeWrapper() {
createService();
mService.systemReady(null);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
index a2d987fb0a8d..f6d6624d7e1c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
@@ -44,15 +44,13 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.internal.matchers.Not;
import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
+import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
@@ -309,22 +307,22 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
public void testRemoveConversationRunnable() throws Exception {
NotificationHistory nh = mock(NotificationHistory.class);
NotificationHistoryDatabase.RemoveConversationRunnable rcr =
- mDataBase.new RemoveConversationRunnable("pkg", "convo");
+ mDataBase.new RemoveConversationRunnable("pkg", Set.of("convo", "another"));
rcr.setNotificationHistory(nh);
AtomicFile af = mock(AtomicFile.class);
when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
mDataBase.mHistoryFiles.addLast(af);
- when(nh.removeConversationFromWrite("pkg", "convo")).thenReturn(true);
+ when(nh.removeConversationsFromWrite("pkg", Set.of("convo", "another"))).thenReturn(true);
mDataBase.mBuffer = mock(NotificationHistory.class);
rcr.run();
- verify(mDataBase.mBuffer).removeConversationFromWrite("pkg", "convo");
+ verify(mDataBase.mBuffer).removeConversationsFromWrite("pkg",Set.of("convo", "another"));
verify(af).openRead();
- verify(nh).removeConversationFromWrite("pkg", "convo");
+ verify(nh).removeConversationsFromWrite("pkg",Set.of("convo", "another"));
verify(af).startWrite();
}
@@ -332,22 +330,22 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
public void testRemoveConversationRunnable_noChanges() throws Exception {
NotificationHistory nh = mock(NotificationHistory.class);
NotificationHistoryDatabase.RemoveConversationRunnable rcr =
- mDataBase.new RemoveConversationRunnable("pkg", "convo");
+ mDataBase.new RemoveConversationRunnable("pkg", Set.of("convo"));
rcr.setNotificationHistory(nh);
AtomicFile af = mock(AtomicFile.class);
when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
mDataBase.mHistoryFiles.addLast(af);
- when(nh.removeConversationFromWrite("pkg", "convo")).thenReturn(false);
+ when(nh.removeConversationsFromWrite("pkg", Set.of("convo"))).thenReturn(false);
mDataBase.mBuffer = mock(NotificationHistory.class);
rcr.run();
- verify(mDataBase.mBuffer).removeConversationFromWrite("pkg", "convo");
+ verify(mDataBase.mBuffer).removeConversationsFromWrite("pkg", Set.of("convo"));
verify(af).openRead();
- verify(nh).removeConversationFromWrite("pkg", "convo");
+ verify(nh).removeConversationsFromWrite("pkg", Set.of("convo"));
verify(af, never()).startWrite();
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
index 2341c10a9c91..a0293b7ad12a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
@@ -47,6 +47,7 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
@RunWith(AndroidJUnit4.class)
@@ -365,15 +366,15 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase {
@Test
public void testDeleteConversation_userUnlocked() {
String pkg = "pkg";
- String convo = "convo";
+ Set<String> convos = Set.of("convo", "another");
NotificationHistoryDatabase userHistory = mock(NotificationHistoryDatabase.class);
mHistoryManager.onUserUnlocked(USER_SYSTEM);
mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistory);
- mHistoryManager.deleteConversation(pkg, 1, convo);
+ mHistoryManager.deleteConversations(pkg, 1, convos);
- verify(userHistory, times(1)).deleteConversation(pkg, convo);
+ verify(userHistory, times(1)).deleteConversations(pkg, convos);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 0be1bf3fe5c2..3e779a9b2435 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -127,6 +127,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
@SmallTest
@@ -3502,8 +3503,9 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testDeleteConversation() {
+ public void testDeleteConversations() {
String convoId = "convo";
+ String convoIdC = "convoC";
NotificationChannel messages =
new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
@@ -3526,10 +3528,16 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel2.setConversationId(calls.getId(), convoId);
mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+ NotificationChannel channel3 =
+ new NotificationChannel("C person msgs", "msgs from C", IMPORTANCE_DEFAULT);
+ channel3.setConversationId(messages.getId(), convoIdC);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel3, true, false);
+
assertEquals(channel, mHelper.getNotificationChannel(PKG_O, UID_O, channel.getId(), false));
assertEquals(channel2,
mHelper.getNotificationChannel(PKG_O, UID_O, channel2.getId(), false));
- assertEquals(2, mHelper.deleteConversation(PKG_O, UID_O, convoId).size());
+ List<String> deleted = mHelper.deleteConversations(PKG_O, UID_O, Set.of(convoId, convoIdC));
+ assertEquals(3, deleted.size());
assertEquals(messages,
mHelper.getNotificationChannel(PKG_O, UID_O, messages.getId(), false));
@@ -3542,7 +3550,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertEquals(channel2,
mHelper.getNotificationChannel(PKG_O, UID_O, channel2.getId(), true));
- assertEquals(7, mLogger.getCalls().size());
+ assertEquals(9, mLogger.getCalls().size());
assertEquals(
NotificationChannelLogger.NotificationChannelEvent.NOTIFICATION_CHANNEL_CREATED,
mLogger.get(0).event); // Channel messages
@@ -3563,12 +3571,20 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mLogger.get(4).event); // Channel channel2 - Conversation A person calls
assertEquals(
NotificationChannelLogger.NotificationChannelEvent
+ .NOTIFICATION_CHANNEL_CONVERSATION_CREATED,
+ mLogger.get(5).event); // Channel channel3 - Conversation C person msgs
+ assertEquals(
+ NotificationChannelLogger.NotificationChannelEvent
+ .NOTIFICATION_CHANNEL_CONVERSATION_DELETED,
+ mLogger.get(6).event); // Delete Channel channel - Conversation A person msgs
+ assertEquals(
+ NotificationChannelLogger.NotificationChannelEvent
.NOTIFICATION_CHANNEL_CONVERSATION_DELETED,
- mLogger.get(5).event); // Delete Channel channel - Conversation A person msgs
+ mLogger.get(7).event); // Delete Channel channel2 - Conversation A person calls
assertEquals(
NotificationChannelLogger.NotificationChannelEvent
.NOTIFICATION_CHANNEL_CONVERSATION_DELETED,
- mLogger.get(6).event); // Delete Channel channel2 - Conversation A person calls
+ mLogger.get(8).event); // Delete Channel channel3 - Conversation C person msgs
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index f10cab87a4fe..3af873d46026 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1522,7 +1522,7 @@ public class ActivityRecordTests extends WindowTestsBase {
try {
// Return error to skip unnecessary operation.
doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay(
- any() /* window */, anyInt() /* seq */, any() /* attrs */,
+ any() /* window */, any() /* attrs */,
anyInt() /* viewVisibility */, anyInt() /* displayId */, any() /* outFrame */,
any() /* outContentInsets */, any() /* outStableInsets */,
any() /* outDisplayCutout */, any() /* outInputChannel */,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 951118125f6e..2920c1da3f24 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -20,6 +20,8 @@ import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.LEFT;
import static android.view.Gravity.RIGHT;
import static android.view.Gravity.TOP;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.ITYPE_TOP_GESTURES;
@@ -218,10 +220,15 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void addingWindow_throwsException_WithMultipleInsetTypes() {
- WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
- win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
+ WindowState win1 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
+ win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
+
+ expectThrows(IllegalArgumentException.class, () -> addWindow(win1));
+
+ WindowState win2 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
+ win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR};
- expectThrows(IllegalArgumentException.class, () -> addWindow(win));
+ expectThrows(IllegalArgumentException.class, () -> addWindow(win2));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index ca739c0dd389..91cfd4e6a89d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -56,4 +56,12 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase {
mImeProvider.scheduleShowImePostLayout(appWin);
assertTrue(mImeProvider.isImeTargetFromDisplayContentAndImeSame());
}
+
+ @Test
+ public void testInputMethodInputTargetCanShowIme() {
+ WindowState target = createWindow(null, TYPE_APPLICATION, "app");
+ mDisplayContent.mInputMethodTarget = target;
+ mImeProvider.scheduleShowImePostLayout(target);
+ assertTrue(mImeProvider.isImeTargetFromDisplayContentAndImeSame());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 4a4974355ea2..e2cd8a909266 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -20,6 +20,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -284,12 +286,17 @@ public class InsetsStateControllerTest extends WindowTestsBase {
public void testBarControllingWinChanged() {
final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ final WindowState climateBar = createWindow(null, TYPE_APPLICATION, "climateBar");
+ final WindowState extraNavBar = createWindow(null, TYPE_APPLICATION, "extraNavBar");
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+ getController().getSourceProvider(ITYPE_CLIMATE_BAR).setWindow(climateBar, null, null);
+ getController().getSourceProvider(ITYPE_EXTRA_NAVIGATION_BAR).setWindow(extraNavBar, null,
+ null);
getController().onBarControlTargetChanged(app, null, app, null);
InsetsSourceControl[] controls = getController().getControlsForDispatch(app);
- assertEquals(2, controls.length);
+ assertEquals(4, controls.length);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 3053fe6ec55f..cc8b2a1bb392 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -173,6 +173,35 @@ public class RootWindowContainerTests extends WindowTestsBase {
}
@Test
+ public void testTaskLayerRank() {
+ final Task rootTask = new TaskBuilder(mSupervisor).build();
+ final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+ new ActivityBuilder(mAtm).setStack(task1).build().mVisibleRequested = true;
+ // RootWindowContainer#invalidateTaskLayers should post to update.
+ waitHandlerIdle(mWm.mH);
+
+ assertEquals(1, task1.mLayerRank);
+ // Only tasks that directly contain activities have a ranking.
+ assertEquals(Task.LAYER_RANK_INVISIBLE, rootTask.mLayerRank);
+
+ final Task task2 = new TaskBuilder(mSupervisor).build();
+ new ActivityBuilder(mAtm).setStack(task2).build().mVisibleRequested = true;
+ waitHandlerIdle(mWm.mH);
+
+ // Note that ensureActivitiesVisible is disabled in SystemServicesTestRule, so both the
+ // activities have the visible rank.
+ assertEquals(2, task1.mLayerRank);
+ // The task2 is the top task, so it has a lower rank as a higher priority oom score.
+ assertEquals(1, task2.mLayerRank);
+
+ task2.moveToBack("test", null /* task */);
+ waitHandlerIdle(mWm.mH);
+
+ assertEquals(1, task1.mLayerRank);
+ assertEquals(2, task2.mLayerRank);
+ }
+
+ @Test
public void testForceStopPackage() {
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
final ActivityRecord activity = task.getTopMostActivity();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index d37f3f402c30..ea1223312cb2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -94,11 +94,6 @@ public class TestIWindow extends IWindow.Stub {
}
@Override
- public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, int localValue,
- int localChanges) throws RemoteException {
- }
-
- @Override
public void dispatchWindowShown() throws RemoteException {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 38c7531f5f5d..e50c00975a7f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -277,6 +277,69 @@ public class WindowProcessControllerTests extends WindowTestsBase {
mWpc.getConfiguration().seq, globalSeq);
}
+ @Test
+ public void testComputeOomAdjFromActivities() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setCreateTask(true)
+ .setUseProcess(mWpc)
+ .build();
+ activity.mVisibleRequested = true;
+ final int[] callbackResult = { 0 };
+ final int visible = 1;
+ final int paused = 2;
+ final int stopping = 4;
+ final int other = 8;
+ final WindowProcessController.ComputeOomAdjCallback callback =
+ new WindowProcessController.ComputeOomAdjCallback() {
+ @Override
+ public void onVisibleActivity() {
+ callbackResult[0] |= visible;
+ }
+
+ @Override
+ public void onPausedActivity() {
+ callbackResult[0] |= paused;
+ }
+
+ @Override
+ public void onStoppingActivity(boolean finishing) {
+ callbackResult[0] |= stopping;
+ }
+
+ @Override
+ public void onOtherActivity() {
+ callbackResult[0] |= other;
+ }
+ };
+
+ // onStartActivity should refresh the state immediately.
+ mWpc.onStartActivity(0 /* topProcessState */, activity.info);
+ assertEquals(1 /* minTaskLayer */, mWpc.computeOomAdjFromActivities(callback));
+ assertEquals(visible, callbackResult[0]);
+
+ // The oom state will be updated in handler from activity state change.
+ callbackResult[0] = 0;
+ activity.mVisibleRequested = false;
+ activity.setState(Task.ActivityState.PAUSED, "test");
+ waitHandlerIdle(mAtm.mH);
+ mWpc.computeOomAdjFromActivities(callback);
+ assertEquals(paused, callbackResult[0]);
+
+ // updateProcessInfo with updateOomAdj=true should refresh the state immediately.
+ callbackResult[0] = 0;
+ activity.setState(Task.ActivityState.STOPPING, "test");
+ mWpc.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* activityChange */, true /* updateOomAdj */, false /* addPendingTopUid */);
+ mWpc.computeOomAdjFromActivities(callback);
+ assertEquals(stopping, callbackResult[0]);
+
+ callbackResult[0] = 0;
+ activity.setState(Task.ActivityState.STOPPED, "test");
+ waitHandlerIdle(mAtm.mH);
+ mWpc.computeOomAdjFromActivities(callback);
+ assertEquals(other, callbackResult[0]);
+ }
+
private TestDisplayContent createTestDisplayContentInContainer() {
return new TestDisplayContent.Builder(mAtm, 1000, 1500).build();
}
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 7daddd8720ab..986807e661f1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -360,7 +360,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
attrs.setTitle(name);
final WindowState w = new WindowState(service, session, iWindow, token, parent,
- OP_NONE, 0, attrs, VISIBLE, ownerId, userId,
+ OP_NONE, attrs, VISIBLE, ownerId, userId,
ownerCanAddInternalSystemWindow,
powerManagerWrapper);
// TODO: Probably better to make this call in the WindowState ctor to avoid errors with
@@ -1088,7 +1088,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
TestWindowState(WindowManagerService service, Session session, IWindow window,
WindowManager.LayoutParams attrs, WindowToken token) {
- super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0, 0,
+ super(service, session, window, token, null, OP_NONE, attrs, 0, 0, 0,
false /* ownerCanAddInternalSystemWindow */);
}
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index f7fe1ba1f998..1472a4ac27bc 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,7 +16,6 @@
package android.telecom;
-import android.annotation.SystemApi;
import android.media.ToneGenerator;
import android.os.Parcel;
import android.os.Parcelable;
@@ -97,10 +96,7 @@ public final class DisconnectCause implements Parcelable {
*
* This reason code is only used for communication between a {@link ConnectionService} and
* Telecom and should not be surfaced to the user.
- *
- * @hide
*/
- @SystemApi
public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
/**
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index c20e5ad8ce7c..4a3561b5d509 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -28,6 +28,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import java.util.ArrayList;
@@ -52,8 +53,17 @@ public final class PhoneAccount implements Parcelable {
*
* This is an extras key set via {@link Builder#setExtras} which determines the order in which
* {@link PhoneAccount}s from the same {@link ConnectionService} are sorted. The accounts
- * are sorted by this key via standard lexicographical order, and this ordering is used to
+ * are sorted by this key via standard lexicographical order, (as implemented in
+ * {@link String#compareTo}), and this ordering is used to
* determine priority when a call can be placed via multiple accounts.
+ *
+ * When multiple {@link PhoneAccount}s are supplied with the same sort order key, no ordering is
+ * guaranteed between those {@link PhoneAccount}s. Additionally, no ordering is guaranteed
+ * between {@link PhoneAccount}s that do not supply this extra, and all such accounts
+ * will be sorted after the accounts that do supply this extra.
+ *
+ * An example of a sort order key is slot index (see {@link TelephonyManager#getSlotIndex()}),
+ * which is the one used by the cell Telephony stack.
* @hide
*/
@SystemApi
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index 0a7e1581815e..72a739c5c879 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -933,11 +933,11 @@ package android.telephony.data {
method public int getSuggestedRetryTime();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
- field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 2; // 0x2
- field public static final int HANDOVER_FAILURE_MODE_LEGACY = 1; // 0x1
- field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 3; // 0x3
- field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 4; // 0x4
- field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = 0; // 0x0
+ field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; // 0x1
+ field public static final int HANDOVER_FAILURE_MODE_LEGACY = 0; // 0x0
+ field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 2; // 0x2
+ field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 3; // 0x3
+ field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = -1; // 0xffffffff
field public static final int LINK_STATUS_ACTIVE = 2; // 0x2
field public static final int LINK_STATUS_DORMANT = 1; // 0x1
field public static final int LINK_STATUS_INACTIVE = 0; // 0x0
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 47bc566a331a..8261b53a2c9f 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -79,6 +79,30 @@ public class CarrierConfigManager {
*/
public static final int SERVICE_CLASS_VOICE = ImsSsData.SERVICE_CLASS_VOICE;
+ /**
+ * Only send USSD over IMS while CS is out of service, otherwise send USSD over CS.
+ * {@link #KEY_CARRIER_USSD_METHOD_INT}
+ */
+ public static final int USSD_OVER_CS_PREFERRED = 0;
+
+ /**
+ * Send USSD over IMS or CS while IMS is out of service or silent redial over CS if needed.
+ * {@link #KEY_CARRIER_USSD_METHOD_INT}
+ */
+ public static final int USSD_OVER_IMS_PREFERRED = 1;
+
+ /**
+ * Only send USSD over CS.
+ * {@link #KEY_CARRIER_USSD_METHOD_INT}
+ */
+ public static final int USSD_OVER_CS_ONLY = 2;
+
+ /**
+ * Only send USSD over IMS and disallow silent redial over CS.
+ * {@link #KEY_CARRIER_USSD_METHOD_INT}
+ */
+ public static final int USSD_OVER_IMS_ONLY = 3;
+
private final Context mContext;
/**
@@ -584,6 +608,20 @@ public class CarrierConfigManager {
public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
/**
+ * Specify the method of selection for UE sending USSD requests. The default value is
+ * {@link #USSD_OVER_CS_PREFERRED}.
+ * <p> Available options:
+ * <ul>
+ * <li>0: {@link #USSD_OVER_CS_PREFERRED} </li>
+ * <li>1: {@link #USSD_OVER_IMS_PREFERRED} </li>
+ * <li>2: {@link #USSD_OVER_CS_ONLY} </li>
+ * <li>3: {@link #USSD_OVER_IMS_ONLY} </li>
+ * </ul>
+ */
+ public static final String KEY_CARRIER_USSD_METHOD_INT =
+ "carrier_ussd_method_int";
+
+ /**
* Flag specifying whether to show an alert dialog for 5G disable when the user disables VoLTE.
* By default this value is {@code false}.
*
@@ -3932,6 +3970,16 @@ public class CarrierConfigManager {
public static final String KEY_DEFAULT_PREFERRED_APN_NAME_STRING =
"default_preferred_apn_name_string";
+ /**
+ * For Android 11, provide a temporary solution for OEMs to use the lower of the two MTU values
+ * for IPv4 and IPv6 if both are sent.
+ * TODO: remove in later release
+ *
+ * @hide
+ */
+ public static final String KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED =
+ "use_lower_mtu_value_if_both_received";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -3953,6 +4001,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
+ sDefaults.putInt(KEY_CARRIER_USSD_METHOD_INT, USSD_OVER_CS_PREFERRED);
sDefaults.putBoolean(KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL, false);
sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_MERGING_RTT_CALLS_BOOL, false);
@@ -4473,6 +4522,7 @@ public class CarrierConfigManager {
sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
+ sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a82d98807ad3..7a0e1cd4d649 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -85,8 +85,6 @@ import android.telephony.emergency.EmergencyNumber;
import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.aidl.IImsConfig;
-import android.telephony.ims.aidl.IImsMmTelFeature;
-import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -94,7 +92,6 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CellNetworkScanResult;
@@ -2807,7 +2804,11 @@ public class TelephonyManager {
/** Current network is LTE_CA {@hide} */
@UnsupportedAppUsage
public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19.
- /** Current network is NR(New Radio) 5G. */
+ /**
+ * Current network is NR (New Radio) 5G.
+ * This will only be returned for 5G SA.
+ * For 5G NSA, the network type will be {@link #NETWORK_TYPE_LTE}.
+ */
public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20.
private static final @NetworkType int[] NETWORK_TYPES = {
@@ -7380,80 +7381,6 @@ public class TelephonyManager {
}
/**
- * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id and MMTel
- * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
- * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
- * @param slotIndex The SIM slot that we are requesting the {@link IImsMmTelFeature} for.
- * @param callback Listener that will send updates to ImsManager when there are updates to
- * ImsServiceController.
- * @return {@link IImsMmTelFeature} interface for the feature specified or {@code null} if
- * it is unavailable.
- * @hide
- */
- public @Nullable IImsMmTelFeature getImsMmTelFeatureAndListen(int slotIndex,
- IImsServiceFeatureCallback callback) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- return telephony.getMmTelFeatureAndListen(slotIndex, callback);
- }
- } catch (RemoteException e) {
- Rlog.e(TAG, "getImsMmTelFeatureAndListen, RemoteException: "
- + e.getMessage());
- }
- return null;
- }
-
- /**
- * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id and RCS
- * feature for emergency calling or {@link null} if the service is not available. If an
- * RcsFeature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
- * listener for feature updates.
- * @param slotIndex The SIM slot that we are requesting the {@link IImsRcsFeature} for.
- * @param callback Listener that will send updates to ImsManager when there are updates to
- * ImsServiceController.
- * @return {@link IImsRcsFeature} interface for the feature specified or {@code null} if
- * it is unavailable.
- * @hide
- */
- public @Nullable IImsRcsFeature getImsRcsFeatureAndListen(int slotIndex,
- IImsServiceFeatureCallback callback) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- return telephony.getRcsFeatureAndListen(slotIndex, callback);
- }
- } catch (RemoteException e) {
- Rlog.e(TAG, "getImsRcsFeatureAndListen, RemoteException: "
- + e.getMessage());
- }
- return null;
- }
-
- /**
- * Unregister a IImsServiceFeatureCallback previously associated with an ImsFeature through
- * {@link #getImsMmTelFeatureAndListen(int, IImsServiceFeatureCallback)} or
- * {@link #getImsRcsFeatureAndListen(int, IImsServiceFeatureCallback)}.
- * @param slotIndex The SIM slot associated with the callback.
- * @param featureType The {@link android.telephony.ims.feature.ImsFeature.FeatureType}
- * associated with the callback.
- * @param callback The callback to be unregistered.
- * @hide
- */
- public void unregisterImsFeatureCallback(int slotIndex, int featureType,
- IImsServiceFeatureCallback callback) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- telephony.unregisterImsFeatureCallback(slotIndex, featureType, callback);
- }
- } catch (RemoteException e) {
- Rlog.e(TAG, "unregisterImsFeatureCallback, RemoteException: "
- + e.getMessage());
- }
- }
-
- /**
* @return the {@IImsRegistration} interface that corresponds with the slot index and feature.
* @param slotIndex The SIM slot corresponding to the ImsService ImsRegistration is active for.
* @param feature An integer indicating the feature that we wish to get the ImsRegistration for.
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 5ead8decdb63..39859b1e4fdb 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -80,33 +80,33 @@ public final class DataCallResponse implements Parcelable {
/**
* Data handover failure mode is unknown.
*/
- public static final int HANDOVER_FAILURE_MODE_UNKNOWN = 0;
+ public static final int HANDOVER_FAILURE_MODE_UNKNOWN = -1;
/**
* Perform fallback to the source data transport on data handover failure using
* the legacy logic, which is fallback if the fail cause is
* {@link DataFailCause#HANDOFF_PREFERENCE_CHANGED}.
*/
- public static final int HANDOVER_FAILURE_MODE_LEGACY = 1;
+ public static final int HANDOVER_FAILURE_MODE_LEGACY = 0;
/**
* Perform fallback to the source data transport on data handover failure.
*/
- public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 2;
+ public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1;
/**
* Do not perform fallback to the source data transport on data handover failure.
- * Frameworks should keep retrying handover by sending
+ * Framework will retry setting up a new data connection by sending
* {@link DataService#REQUEST_REASON_HANDOVER} request to the underlying {@link DataService}.
*/
- public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 3;
+ public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 2;
/**
* Do not perform fallback to the source transport on data handover failure.
- * Frameworks should retry setup a new data connection by sending
+ * Framework will retry setting up a new data connection by sending
* {@link DataService#REQUEST_REASON_NORMAL} request to the underlying {@link DataService}.
*/
- public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 4;
+ public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 3;
private final @DataFailureCause int mCause;
private final int mSuggestedRetryTime;
@@ -332,6 +332,7 @@ public final class DataCallResponse implements Parcelable {
.append(" mtu=").append(getMtu())
.append(" mtuV4=").append(getMtuV4())
.append(" mtuV6=").append(getMtuV6())
+ .append(" handoverFailureMode=").append(getHandoverFailureMode())
.append("}");
return sb.toString();
}
@@ -361,14 +362,15 @@ public final class DataCallResponse implements Parcelable {
&& mPcscfAddresses.containsAll(other.mPcscfAddresses)
&& mMtu == other.mMtu
&& mMtuV4 == other.mMtuV4
- && mMtuV6 == other.mMtuV6;
+ && mMtuV6 == other.mMtuV6
+ && mHandoverFailureMode == other.mHandoverFailureMode;
}
@Override
public int hashCode() {
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
- mMtu, mMtuV4, mMtuV6);
+ mMtu, mMtuV4, mMtuV6, mHandoverFailureMode);
}
@Override
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index f6c14e67306b..ee2fce7e7dd5 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -59,6 +59,7 @@ import java.util.function.Consumer;
* manager.
*/
public class ImsMmTelManager implements RegistrationManager {
+ private static final String TAG = "ImsMmTelManager";
/**
* @hide
@@ -809,7 +810,7 @@ public class ImsMmTelManager implements RegistrationManager {
}
try {
- getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
+ iTelephony.isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
@Override
public void accept(int result) {
executor.execute(() -> callback.accept(result == 1));
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index da7311c08307..8a05bdfc8401 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -16,6 +16,7 @@
package android.telephony.ims;
+import android.annotation.LongDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Service;
@@ -41,6 +42,11 @@ import android.util.SparseArray;
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
* ImsService must register the service in their AndroidManifest to be detected by the framework.
@@ -98,6 +104,32 @@ public class ImsService extends Service {
private static final String LOG_TAG = "ImsService";
/**
+ * This ImsService supports the capability to place emergency calls over MMTEL.
+ * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be
+ * adding other capabilities in a central location, so track this capability here as well.
+ */
+ public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0;
+
+ /**
+ * @hide
+ */
+ @LongDef(flag = true,
+ prefix = "CAPABILITY_",
+ value = {
+ CAPABILITY_EMERGENCY_OVER_MMTEL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsServiceCapability {}
+
+ /**
+ * Used for logging purposes, see {@link #getCapabilitiesString(long)}
+ * @hide
+ */
+ private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{
+ put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL");
+ }};
+
+ /**
* The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
* @hide
*/
@@ -409,4 +441,30 @@ public class ImsService extends Service {
public ImsRegistrationImplBase getRegistration(int slotId) {
return new ImsRegistrationImplBase();
}
+
+ /**
+ * @return A string representation of the ImsService capabilties for logging.
+ * @hide
+ */
+ public static String getCapabilitiesString(@ImsServiceCapability long caps) {
+ StringBuffer result = new StringBuffer();
+ result.append("capabilities={ ");
+ // filter incrementally fills 0s from left to right. This is used to keep filtering out
+ // more bits in the long until the remaining leftmost bits are all zero.
+ long filter = 0xFFFFFFFFFFFFFFFFL;
+ // position of iterator to potentially print capability.
+ long i = 0;
+ while ((caps & filter) != 0 && i <= 63) {
+ long bitToCheck = (1L << i);
+ if ((caps & bitToCheck) != 0) {
+ result.append(CAPABILITIES_LOG_MAP.getOrDefault(bitToCheck, bitToCheck + "?"));
+ result.append(" ");
+ }
+ // shift left by one and fill in another 1 on the leftmost bit.
+ filter <<= 1;
+ i++;
+ }
+ result.append("}");
+ return result.toString();
+ }
} \ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 9e461420e126..d012703b7510 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -22,6 +22,7 @@ import android.telephony.ims.aidl.IRcsUceControllerCallback;
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
import android.telephony.ims.aidl.IImsRegistrationCallback;
+import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.IIntegerConsumer;
/**
@@ -50,4 +51,9 @@ interface IImsRcsController {
void setUceSettingEnabled(int subId, boolean isEnabled);
void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
+
+ // Internal commands that should not be made public
+ void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback,
+ boolean oneShot);
+ void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback);
}
diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.aidl b/telephony/java/com/android/ims/ImsFeatureContainer.aidl
new file mode 100644
index 000000000000..9706f20c59ca
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsFeatureContainer.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+parcelable ImsFeatureContainer; \ No newline at end of file
diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.java b/telephony/java/com/android/ims/ImsFeatureContainer.java
new file mode 100644
index 000000000000..b259679ea1bf
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsFeatureContainer.java
@@ -0,0 +1,172 @@
+/*
+ * 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.ims;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.feature.ImsFeature;
+
+import java.util.Objects;
+
+/**
+ * Contains an IBinder linking to the appropriate ImsFeature as well as the associated
+ * interfaces.
+ * @hide
+ */
+public final class ImsFeatureContainer implements Parcelable {
+ /**
+ * ImsFeature that is being tracked.
+ */
+ public final IBinder imsFeature;
+
+ /**
+ * IImsConfig interface that should be associated with the ImsFeature.
+ */
+ public final android.telephony.ims.aidl.IImsConfig imsConfig;
+
+ /**
+ * IImsRegistration interface that should be associated with this ImsFeature.
+ */
+ public final IImsRegistration imsRegistration;
+
+ /**
+ * State of the feature that is being tracked.
+ */
+ private @ImsFeature.ImsState int mState = ImsFeature.STATE_UNAVAILABLE;
+
+ /**
+ * Capabilities of this ImsService.
+ */
+ private @ImsService.ImsServiceCapability long mCapabilities;
+ /**
+ * Contains the ImsFeature IBinder as well as the ImsService interfaces associated with
+ * that feature.
+ * @param iFace IBinder connection to the ImsFeature.
+ * @param iConfig IImsConfig interface associated with the ImsFeature.
+ * @param iReg IImsRegistration interface associated with the ImsFeature
+ * @param initialCaps The initial capabilities that the ImsService supports.
+ */
+ public ImsFeatureContainer(@NonNull IBinder iFace, @NonNull IImsConfig iConfig,
+ @NonNull IImsRegistration iReg, long initialCaps) {
+ imsFeature = iFace;
+ imsConfig = iConfig;
+ imsRegistration = iReg;
+ mCapabilities = initialCaps;
+ }
+
+ /**
+ * Create an ImsFeatureContainer from a Parcel.
+ */
+ private ImsFeatureContainer(Parcel in) {
+ imsFeature = in.readStrongBinder();
+ imsConfig = IImsConfig.Stub.asInterface(in.readStrongBinder());
+ imsRegistration = IImsRegistration.Stub.asInterface(in.readStrongBinder());
+ mState = in.readInt();
+ mCapabilities = in.readLong();
+ }
+
+ /**
+ * @return the capabilties that are associated with the ImsService that this ImsFeature
+ * belongs to.
+ */
+ public @ImsService.ImsServiceCapability long getCapabilities() {
+ return mCapabilities;
+ }
+
+ /**
+ * Update the capabilities that are associated with the ImsService that this ImsFeature
+ * belongs to.
+ */
+ public void setCapabilities(@ImsService.ImsServiceCapability long caps) {
+ mCapabilities = caps;
+ }
+
+ /**
+ * @return The state of the ImsFeature.
+ */
+ public @ImsFeature.ImsState int getState() {
+ return mState;
+ }
+
+ /**
+ * Set the state that is associated with the ImsService that this ImsFeature
+ * belongs to.
+ */
+ public void setState(@ImsFeature.ImsState int state) {
+ mState = state;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ImsFeatureContainer that = (ImsFeatureContainer) o;
+ return imsFeature.equals(that.imsFeature) &&
+ imsConfig.equals(that.imsConfig) &&
+ imsRegistration.equals(that.imsRegistration) &&
+ mState == that.getState() &&
+ mCapabilities == that.getCapabilities();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(imsFeature, imsConfig, imsRegistration, mState, mCapabilities);
+ }
+
+ @Override
+ public String toString() {
+ return "FeatureContainer{" +
+ "imsFeature=" + imsFeature +
+ ", imsConfig=" + imsConfig +
+ ", imsRegistration=" + imsRegistration +
+ ", state=" + ImsFeature.STATE_LOG_MAP.get(mState) +
+ ", capabilities = " + ImsService.getCapabilitiesString(mCapabilities) +
+ '}';
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(imsFeature);
+ dest.writeStrongInterface(imsConfig);
+ dest.writeStrongInterface(imsRegistration);
+ dest.writeInt(mState);
+ dest.writeLong(mCapabilities);
+ }
+
+
+ public static final Creator<ImsFeatureContainer> CREATOR = new Creator<ImsFeatureContainer>() {
+ @Override
+ public ImsFeatureContainer createFromParcel(Parcel source) {
+ return new ImsFeatureContainer(source);
+ }
+
+ @Override
+ public ImsFeatureContainer[] newArray(int size) {
+ return new ImsFeatureContainer[size];
+ }
+ };
+}
diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
index 9a9cf5325310..f5f67bd36ec3 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
@@ -16,13 +16,18 @@
package com.android.ims.internal;
+import com.android.ims.ImsFeatureContainer;
/**
- * Interface from ImsResolver to ImsServiceProxy in ImsManager.
- * Callback to ImsManager when a feature changes in the ImsServiceController.
+ * Interface from ImsResolver to FeatureConnections.
+ * Callback to FeatureConnections when a feature's status changes.
* {@hide}
*/
oneway interface IImsServiceFeatureCallback {
- void imsFeatureCreated(int slotId, int feature);
- void imsFeatureRemoved(int slotId, int feature);
- void imsStatusChanged(int slotId, int feature, int status);
+ void imsFeatureCreated(in ImsFeatureContainer feature);
+ // Reason defined in FeatureConnector.UnavailableReason
+ void imsFeatureRemoved(int reason);
+ // Status defined in ImsFeature.ImsState.
+ void imsStatusChanged(int status);
+ //Capabilities defined in ImsService.ImsServiceCapability
+ void updateCapabilities(long capabilities);
} \ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 02a74ba53ccb..ef5078da76ce 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -829,22 +829,15 @@ interface ITelephony {
* as well as registering the MmTelFeature for callbacks using the IImsServiceFeatureCallback
* interface.
*/
- IImsMmTelFeature getMmTelFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
-
- /**
- * Get IImsRcsFeature binder from ImsResolver that corresponds to the subId and RCS feature
- * as well as registering the RcsFeature for callbacks using the IImsServiceFeatureCallback
- * interface.
- */
- IImsRcsFeature getRcsFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
+ void registerMmTelFeatureCallback(int slotId, in IImsServiceFeatureCallback callback,
+ boolean oneShot);
/**
* Unregister a callback that was previously registered through
- * {@link #getMmTelFeatureAndListen} or {@link #getRcsFeatureAndListen}. This should always be
- * called when the callback is no longer being used.
+ * {@link #registerMmTelFeatureCallback}. This should always be called when the callback is no
+ * longer being used.
*/
- void unregisterImsFeatureCallback(int slotId, int featureType,
- in IImsServiceFeatureCallback callback);
+ void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback);
/**
* Returns the IImsRegistration associated with the slot and feature specified.
diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp
new file mode 100644
index 000000000000..647da2abd213
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/Android.bp
@@ -0,0 +1,58 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "SurfaceViewBufferTests",
+ srcs: ["**/*.java","**/*.kt"],
+ manifest: "AndroidManifest.xml",
+ test_config: "AndroidTest.xml",
+ platform_apis: true,
+ certificate: "platform",
+ use_embedded_native_libs: true,
+ jni_libs: [
+ "libsurface_jni",
+ ],
+
+ static_libs: [
+ "androidx.appcompat_appcompat",
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "androidx.test.ext.junit",
+ "kotlin-stdlib",
+ "kotlinx-coroutines-android",
+ "flickerlib",
+ "truth-prebuilt",
+ ],
+}
+
+cc_library_shared {
+ name: "libsurface_jni",
+ srcs: [
+ "cpp/SurfaceProxy.cpp",
+ ],
+ shared_libs: [
+ "libutils",
+ "libgui",
+ "liblog",
+ "libandroid",
+ ],
+ include_dirs: [
+ "system/core/include"
+ ],
+ stl: "libc++_static",
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
diff --git a/tests/SurfaceViewBufferTests/AndroidManifest.xml b/tests/SurfaceViewBufferTests/AndroidManifest.xml
new file mode 100644
index 000000000000..95885c1ca635
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/AndroidManifest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test">
+
+ <uses-sdk android:minSdkVersion="29"
+ android:targetSdkVersion="29"/>
+ <!-- Enable / Disable tracing !-->
+ <uses-permission android:name="android.permission.DUMP" />
+ <!-- Enable / Disable sv blast adapter !-->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+
+ <application android:allowBackup="false"
+ android:supportsRtl="true">
+ <activity android:name=".MainActivity"
+ android:taskAffinity="com.android.test.MainActivity"
+ android:theme="@style/AppTheme"
+ android:label="SurfaceViewBufferTestApp"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test"
+ android:label="SurfaceViewBufferTests">
+ </instrumentation>
+</manifest>
diff --git a/tests/SurfaceViewBufferTests/AndroidTest.xml b/tests/SurfaceViewBufferTests/AndroidTest.xml
new file mode 100644
index 000000000000..b73fe4853ecf
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs SurfaceView Buffer Tests">
+ <option name="test-tag" value="SurfaceViewBufferTests" />
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on" />
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="false"/>
+ <option name="test-file-name" value="SurfaceViewBufferTests.apk"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.test"/>
+ <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
+ <option name="shell-timeout" value="6600s" />
+ <option name="test-timeout" value="6000s" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
new file mode 100644
index 000000000000..0c86524293e7
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/log.h>
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+#include <android/window.h>
+#include <gui/Surface.h>
+#include <jni.h>
+#include <system/window.h>
+#include <utils/RefBase.h>
+#include <cassert>
+#include <chrono>
+#include <thread>
+
+#define TAG "SurfaceViewBufferTests"
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
+
+extern "C" {
+int i = 0;
+static ANativeWindow* sAnw;
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_setSurface(JNIEnv* env, jclass,
+ jobject surfaceObject) {
+ sAnw = ANativeWindow_fromSurface(env, surfaceObject);
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+ surface->enableFrameTimestamps(true);
+ return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_waitUntilBufferDisplayed(
+ JNIEnv*, jclass, jint jFrameNumber, jint timeoutSec) {
+ using namespace std::chrono_literals;
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+
+ uint64_t frameNumber = static_cast<uint64_t>(jFrameNumber);
+ nsecs_t outRequestedPresentTime, outAcquireTime, outLatchTime, outFirstRefreshStartTime;
+ nsecs_t outLastRefreshStartTime, outGlCompositionDoneTime, outDequeueReadyTime;
+ nsecs_t outDisplayPresentTime = -1;
+ nsecs_t outReleaseTime;
+
+ auto start = std::chrono::steady_clock::now();
+ while (outDisplayPresentTime < 0) {
+ std::this_thread::sleep_for(8ms);
+ surface->getFrameTimestamps(frameNumber, &outRequestedPresentTime, &outAcquireTime,
+ &outLatchTime, &outFirstRefreshStartTime,
+ &outLastRefreshStartTime, &outGlCompositionDoneTime,
+ &outDisplayPresentTime, &outDequeueReadyTime, &outReleaseTime);
+ if (outDisplayPresentTime < 0) {
+ auto end = std::chrono::steady_clock::now();
+ if (std::chrono::duration_cast<std::chrono::seconds>(end - start).count() >
+ timeoutSec) {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_draw(JNIEnv*, jclass) {
+ assert(sAnw);
+ ANativeWindow_Buffer outBuffer;
+ ANativeWindow_lock(sAnw, &outBuffer, nullptr);
+ return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowLock(JNIEnv*, jclass) {
+ assert(sAnw);
+ ANativeWindow_Buffer outBuffer;
+ ANativeWindow_lock(sAnw, &outBuffer, nullptr);
+ return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowUnlockAndPost(JNIEnv*,
+ jclass) {
+ assert(sAnw);
+ ANativeWindow_unlockAndPost(sAnw);
+ return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowSetBuffersGeometry(
+ JNIEnv* /* env */, jclass /* clazz */, jobject /* surfaceObject */, jint w, jint h,
+ jint format) {
+ assert(sAnw);
+ return ANativeWindow_setBuffersGeometry(sAnw, w, h, format);
+}
+} \ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/res/values/styles.xml b/tests/SurfaceViewBufferTests/res/values/styles.xml
new file mode 100644
index 000000000000..8b50738a06de
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/res/values/styles.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <item name="windowNoTitle">true</item>
+ <item name="windowActionBar">false</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowDisablePreview">true</item>
+</style>
+</resources>
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt b/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
new file mode 100644
index 000000000000..b1e1336c4f6d
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.test
+
+import android.app.Activity
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Rect
+import android.os.Bundle
+import android.view.Gravity
+import android.view.Surface
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+import android.widget.FrameLayout
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.concurrent.withLock
+
+class MainActivity : Activity() {
+ val mSurfaceProxy = SurfaceProxy()
+ private var mSurfaceHolder: SurfaceHolder? = null
+ private val mDrawLock = ReentrantLock()
+
+ val surface: Surface? get() = mSurfaceHolder?.surface
+
+ public override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ addSurfaceView(Rect(0, 0, 500, 200))
+ }
+
+ fun addSurfaceView(size: Rect): CountDownLatch {
+ val layout = findViewById<FrameLayout>(android.R.id.content)
+ val surfaceReadyLatch = CountDownLatch(1)
+ val surfaceView = createSurfaceView(applicationContext, size, surfaceReadyLatch)
+ layout.addView(surfaceView,
+ FrameLayout.LayoutParams(size.width(), size.height(), Gravity.TOP or Gravity.LEFT)
+ .also { it.setMargins(100, 100, 0, 0) })
+ return surfaceReadyLatch
+ }
+
+ private fun createSurfaceView(
+ context: Context,
+ size: Rect,
+ surfaceReadyLatch: CountDownLatch
+ ): SurfaceView {
+ val surfaceView = SurfaceView(context)
+ surfaceView.setWillNotDraw(false)
+ surfaceView.holder.setFixedSize(size.width(), size.height())
+ surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
+ override fun surfaceCreated(holder: SurfaceHolder) {
+ mDrawLock.withLock {
+ mSurfaceHolder = holder
+ mSurfaceProxy.setSurface(holder.surface)
+ }
+ surfaceReadyLatch.countDown()
+ }
+
+ override fun surfaceChanged(
+ holder: SurfaceHolder,
+ format: Int,
+ width: Int,
+ height: Int
+ ) {
+ }
+
+ override fun surfaceDestroyed(holder: SurfaceHolder) {
+ mDrawLock.withLock {
+ mSurfaceHolder = null
+ }
+ }
+ })
+ return surfaceView
+ }
+
+ fun drawFrame(): Rect {
+ mDrawLock.withLock {
+ val holder = mSurfaceHolder ?: return Rect()
+ val canvas = holder.lockCanvas()
+ val canvasSize = Rect(0, 0, canvas.width, canvas.height)
+ canvas.drawColor(Color.GREEN)
+ val p = Paint()
+ p.color = Color.RED
+ canvas.drawRect(canvasSize, p)
+ holder.unlockCanvasAndPost(canvas)
+ return canvasSize
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
new file mode 100644
index 000000000000..884aae41446c
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test
+
+class SurfaceProxy {
+ init {
+ System.loadLibrary("surface_jni")
+ }
+
+ external fun setSurface(surface: Any)
+ external fun waitUntilBufferDisplayed(frameNumber: Int, timeoutSec: Int)
+ external fun draw()
+
+ // android/native_window.h functions
+ external fun ANativeWindowLock()
+ external fun ANativeWindowUnlockAndPost()
+ external fun ANativeWindowSetBuffersGeometry(surface: Any, width: Int, height: Int, format: Int)
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt
new file mode 100644
index 000000000000..b48a91d49b91
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt
@@ -0,0 +1,185 @@
+/*
+ * 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.test
+
+import android.app.Instrumentation
+import android.graphics.Rect
+import android.provider.Settings
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.monitor.LayersTraceMonitor
+import com.android.server.wm.flicker.monitor.withSFTracing
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.util.concurrent.CountDownLatch
+import kotlin.properties.Delegates
+
+@RunWith(Parameterized::class)
+class SurfaceViewBufferTest(val useBlastAdapter: Boolean) {
+ private var mInitialUseBlastConfig by Delegates.notNull<Int>()
+
+ @get:Rule
+ var scenarioRule: ActivityScenarioRule<MainActivity> =
+ ActivityScenarioRule<MainActivity>(MainActivity::class.java)
+
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val defaultBufferSize = Rect(0, 0, 640, 480)
+
+ @Before
+ fun setup() {
+ mInitialUseBlastConfig = Settings.Global.getInt(instrumentation.context.contentResolver,
+ "use_blast_adapter_sv", 0)
+ val enable = if (useBlastAdapter) 1 else 0
+ Settings.Global.putInt(instrumentation.context.contentResolver, "use_blast_adapter_sv",
+ enable)
+ val tmpDir = instrumentation.targetContext.dataDir.toPath()
+ LayersTraceMonitor(tmpDir).stop()
+
+ lateinit var surfaceReadyLatch: CountDownLatch
+ scenarioRule.getScenario().onActivity {
+ surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
+ }
+ surfaceReadyLatch.await()
+ }
+
+ @After
+ fun teardown() {
+ scenarioRule.getScenario().close()
+ Settings.Global.putInt(instrumentation.context.contentResolver,
+ "use_blast_adapter_sv", mInitialUseBlastConfig)
+ }
+
+ @Test
+ fun testSetBuffersGeometry_0x0_resetsBufferSize() {
+ val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
+ scenarioRule.getScenario().onActivity {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0,
+ R8G8B8A8_UNORM)
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
+ }
+ }
+
+ // verify buffer size is reset to default buffer size
+ assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+ }
+
+ @Test
+ fun testSetBuffersGeometry_0x0_rejectsBuffer() {
+ val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
+ scenarioRule.getScenario().onActivity {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
+ R8G8B8A8_UNORM)
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0, R8G8B8A8_UNORM)
+ // Submit buffer one with a different size which should be rejected
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+
+ // submit a buffer with the default buffer size
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(3, 1 /* sec */)
+ }
+ }
+ // Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE
+ assertThat(trace).layer("SurfaceView", 2).doesNotExist()
+
+ // Verify the next buffer is submitted with the correct size
+ assertThat(trace).layer("SurfaceView", 3).also {
+ it.hasBufferSize(defaultBufferSize)
+ it.hasScalingMode(0 /* NATIVE_WINDOW_SCALING_MODE_FREEZE */)
+ }
+ }
+
+ @Test
+ fun testSetBuffersGeometry_smallerThanBuffer() {
+ val bufferSize = Rect(0, 0, 300, 200)
+ val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
+ scenarioRule.getScenario().onActivity {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize.width(),
+ bufferSize.height(), R8G8B8A8_UNORM)
+ it.drawFrame()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
+ }
+ }
+
+ assertThat(trace).layer("SurfaceView", 1).also {
+ it.hasBufferSize(bufferSize)
+ it.hasLayerSize(defaultBufferSize)
+ it.hasScalingMode(1 /* NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW */)
+ }
+ }
+
+ @Test
+ fun testSetBuffersGeometry_largerThanBuffer() {
+ val bufferSize = Rect(0, 0, 3000, 2000)
+ val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
+ scenarioRule.getScenario().onActivity {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize.width(),
+ bufferSize.height(), R8G8B8A8_UNORM)
+ it.drawFrame()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
+ }
+ }
+
+ assertThat(trace).layer("SurfaceView", 1).also {
+ it.hasBufferSize(bufferSize)
+ it.hasLayerSize(defaultBufferSize)
+ it.hasScalingMode(1 /* NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW */)
+ }
+ }
+
+ /** Submit buffers as fast as possible and make sure they are queued */
+ @Test
+ fun testQueueBuffers() {
+ val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
+ scenarioRule.getScenario().onActivity {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
+ R8G8B8A8_UNORM)
+ for (i in 0..100) {
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ }
+ it.mSurfaceProxy.waitUntilBufferDisplayed(100, 1 /* sec */)
+ }
+ }
+ for (frameNumber in 1..100) {
+ assertThat(trace).layer("SurfaceView", frameNumber.toLong())
+ }
+ }
+
+ companion object {
+ private const val TRACE_FLAGS = 0x1 // TRACE_CRITICAL
+ private const val R8G8B8A8_UNORM = 1
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "blast={0}")
+ fun data(): Collection<Array<Any>> {
+ return listOf(
+ arrayOf(false), // First test: submit buffers via bufferqueue
+ arrayOf(true) // Second test: submit buffers via blast adapter
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 124b6609f687..0fe84abcbc7b 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -63,6 +63,7 @@ android_test {
"services.net",
],
libs: [
+ "android.net.ipsec.ike.stubs.module_lib",
"android.test.runner",
"android.test.base",
"android.test.mock",
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index 3c3076f11727..030ddd2792bb 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -32,7 +32,6 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.net.LinkProperties.ProvisioningChange;
-import android.net.util.LinkPropertiesUtils.CompareResult;
import android.os.Build;
import android.system.OsConstants;
import android.util.ArraySet;
@@ -41,6 +40,7 @@ import androidx.core.os.BuildCompat;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 91c9a2a38036..6de31f6b4be1 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -22,11 +22,11 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.net.util.MacAddressUtils;
-
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.MacAddressUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index c76b4cd501e7..c3f1549409fc 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -20,6 +20,7 @@ import static android.content.pm.UserInfo.FLAG_ADMIN;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static android.content.pm.UserInfo.FLAG_PRIMARY;
import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+import static android.net.ConnectivityManager.NetworkCallback;
import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
@@ -45,7 +46,9 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -66,6 +69,7 @@ import android.net.Ikev2VpnProfile;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.IpSecManager;
+import android.net.IpSecTunnelInterfaceResponse;
import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.Network;
@@ -75,6 +79,8 @@ import android.net.RouteInfo;
import android.net.UidRange;
import android.net.VpnManager;
import android.net.VpnService;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.ConditionVariable;
@@ -101,6 +107,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -150,6 +157,11 @@ public class VpnTest {
private static final String TEST_VPN_IDENTITY = "identity";
private static final byte[] TEST_VPN_PSK = "psk".getBytes();
+ private static final Network TEST_NETWORK = new Network(Integer.MAX_VALUE);
+ private static final String TEST_IFACE_NAME = "TEST_IFACE";
+ private static final int TEST_TUNNEL_RESOURCE_ID = 0x2345;
+ private static final long TEST_TIMEOUT_MS = 500L;
+
/**
* Names and UIDs for some fake packages. Important points:
* - UID is ordered increasing.
@@ -227,6 +239,13 @@ public class VpnTest {
// Deny all appops by default.
when(mAppOps.noteOpNoThrow(anyInt(), anyInt(), anyString()))
.thenReturn(AppOpsManager.MODE_IGNORED);
+
+ // Setup IpSecService
+ final IpSecTunnelInterfaceResponse tunnelResp =
+ new IpSecTunnelInterfaceResponse(
+ IpSecManager.Status.OK, TEST_TUNNEL_RESOURCE_ID, TEST_IFACE_NAME);
+ when(mIpSecService.createTunnelInterface(any(), any(), any(), any(), any()))
+ .thenReturn(tunnelResp);
}
@Test
@@ -988,6 +1007,52 @@ public class VpnTest {
eq(AppOpsManager.MODE_IGNORED));
}
+ private NetworkCallback triggerOnAvailableAndGetCallback() {
+ final ArgumentCaptor<NetworkCallback> networkCallbackCaptor =
+ ArgumentCaptor.forClass(NetworkCallback.class);
+ verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
+ .requestNetwork(any(), networkCallbackCaptor.capture());
+
+ final NetworkCallback cb = networkCallbackCaptor.getValue();
+ cb.onAvailable(TEST_NETWORK);
+ return cb;
+ }
+
+ @Test
+ public void testStartPlatformVpnAuthenticationFailed() throws Exception {
+ final ArgumentCaptor<IkeSessionCallback> captor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+ final IkeProtocolException exception = mock(IkeProtocolException.class);
+ when(exception.getErrorType())
+ .thenReturn(IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED);
+
+ final Vpn vpn = startLegacyVpn(mVpnProfile);
+ final NetworkCallback cb = triggerOnAvailableAndGetCallback();
+
+ // Wait for createIkeSession() to be called before proceeding in order to ensure consistent
+ // state
+ verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS))
+ .createIkeSession(any(), any(), any(), any(), captor.capture(), any());
+ final IkeSessionCallback ikeCb = captor.getValue();
+ ikeCb.onClosedExceptionally(exception);
+
+ verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb));
+ assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState());
+ }
+
+ @Test
+ public void testStartPlatformVpnIllegalArgumentExceptionInSetup() throws Exception {
+ when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any()))
+ .thenThrow(new IllegalArgumentException());
+ final Vpn vpn = startLegacyVpn(mVpnProfile);
+ final NetworkCallback cb = triggerOnAvailableAndGetCallback();
+
+ // Wait for createIkeSession() to be called before proceeding in order to ensure consistent
+ // state
+ verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb));
+ assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState());
+ }
+
private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) {
assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null, mKeyStore));
diff --git a/tools/xmlpersistence/Android.bp b/tools/xmlpersistence/Android.bp
new file mode 100644
index 000000000000..d58d0dcdc45a
--- /dev/null
+++ b/tools/xmlpersistence/Android.bp
@@ -0,0 +1,11 @@
+java_binary_host {
+ name: "xmlpersistence_cli",
+ manifest: "manifest.txt",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "javaparser-symbol-solver",
+ "javapoet",
+ ],
+}
diff --git a/tools/xmlpersistence/OWNERS b/tools/xmlpersistence/OWNERS
new file mode 100644
index 000000000000..4f4d06a32676
--- /dev/null
+++ b/tools/xmlpersistence/OWNERS
@@ -0,0 +1 @@
+zhanghai@google.com
diff --git a/tools/xmlpersistence/manifest.txt b/tools/xmlpersistence/manifest.txt
new file mode 100644
index 000000000000..6d9771998efc
--- /dev/null
+++ b/tools/xmlpersistence/manifest.txt
@@ -0,0 +1 @@
+Main-class: MainKt
diff --git a/tools/xmlpersistence/src/main/kotlin/Generator.kt b/tools/xmlpersistence/src/main/kotlin/Generator.kt
new file mode 100644
index 000000000000..28467b7fc0b0
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/Generator.kt
@@ -0,0 +1,578 @@
+/*
+ * 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.
+ */
+
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.NameAllocator
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeSpec
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.nio.charset.StandardCharsets
+import java.time.Year
+import java.util.Objects
+import javax.lang.model.element.Modifier
+
+// JavaPoet only supports line comments, and can't add a newline after file level comments.
+val FILE_HEADER = """
+ /*
+ * Copyright (C) ${Year.now().value} The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ // Generated by xmlpersistence. DO NOT MODIFY!
+ // CHECKSTYLE:OFF
+ // @formatter:off
+""".trimIndent() + "\n\n"
+
+private val atomicFileType = ClassName.get("android.util", "AtomicFile")
+
+fun generate(persistence: PersistenceInfo): JavaFile {
+ val distinctClassFields = persistence.root.allClassFields.distinctBy { it.type }
+ val type = TypeSpec.classBuilder(persistence.name)
+ .addJavadoc(
+ """
+ Generated class implementing XML persistence for${'$'}W{@link $1T}.
+ <p>
+ This class provides atomicity for persistence via {@link $2T}, however it does not provide
+ thread safety, so please bring your own synchronization mechanism.
+ """.trimIndent(), persistence.root.type, atomicFileType
+ )
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addField(generateFileField())
+ .addMethod(generateConstructor())
+ .addMethod(generateReadMethod(persistence.root))
+ .addMethod(generateParseMethod(persistence.root))
+ .addMethods(distinctClassFields.map { generateParseClassMethod(it) })
+ .addMethod(generateWriteMethod(persistence.root))
+ .addMethod(generateSerializeMethod(persistence.root))
+ .addMethods(distinctClassFields.map { generateSerializeClassMethod(it) })
+ .addMethod(generateDeleteMethod())
+ .build()
+ return JavaFile.builder(persistence.root.type.packageName(), type)
+ .skipJavaLangImports(true)
+ .indent(" ")
+ .build()
+}
+
+private val nonNullType = ClassName.get("android.annotation", "NonNull")
+
+private fun generateFileField(): FieldSpec =
+ FieldSpec.builder(atomicFileType, "mFile", Modifier.PRIVATE, Modifier.FINAL)
+ .addAnnotation(nonNullType)
+ .build()
+
+private fun generateConstructor(): MethodSpec =
+ MethodSpec.constructorBuilder()
+ .addJavadoc(
+ """
+ Create an instance of this class.
+
+ @param file the XML file for persistence
+ """.trimIndent()
+ )
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(
+ ParameterSpec.builder(File::class.java, "file").addAnnotation(nonNullType).build()
+ )
+ .addStatement("mFile = new \$1T(file)", atomicFileType)
+ .build()
+
+private val nullableType = ClassName.get("android.annotation", "Nullable")
+
+private val xmlPullParserType = ClassName.get("org.xmlpull.v1", "XmlPullParser")
+
+private val xmlType = ClassName.get("android.util", "Xml")
+
+private val xmlPullParserExceptionType = ClassName.get("org.xmlpull.v1", "XmlPullParserException")
+
+private fun generateReadMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("read")
+ .addJavadoc(
+ """
+ Read${'$'}W{@link $1T}${'$'}Wfrom${'$'}Wthe${'$'}WXML${'$'}Wfile.
+
+ @return the persisted${'$'}W{@link $1T},${'$'}Wor${'$'}W{@code null}${'$'}Wif${'$'}Wthe${'$'}WXML${'$'}Wfile${'$'}Wdoesn't${'$'}Wexist
+ @throws IllegalArgumentException if an error occurred while reading
+ """.trimIndent(), rootField.type
+ )
+ .addAnnotation(nullableType)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(rootField.type)
+ .addControlFlow(
+ "try (final \$1T inputStream = mFile.openRead())", FileInputStream::class.java
+ ) {
+ addStatement("final \$1T parser = \$2T.newPullParser()", xmlPullParserType, xmlType)
+ addStatement("parser.setInput(inputStream, null)")
+ addStatement("return parse(parser)")
+ nextControlFlow("catch (\$1T e)", FileNotFoundException::class.java)
+ addStatement("return null")
+ nextControlFlow(
+ "catch (\$1T | \$2T e)", IOException::class.java, xmlPullParserExceptionType
+ )
+ addStatement("throw new IllegalArgumentException(e)")
+ }
+ .build()
+
+private val ClassFieldInfo.allClassFields: List<ClassFieldInfo>
+ get() =
+ mutableListOf<ClassFieldInfo>().apply {
+ this += this@allClassFields
+ for (field in fields) {
+ when (field) {
+ is ClassFieldInfo -> this += field.allClassFields
+ is ListFieldInfo -> this += field.element.allClassFields
+ }
+ }
+ }
+
+private fun generateParseMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("parse")
+ .addAnnotation(nonNullType)
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .returns(rootField.type)
+ .addParameter(
+ ParameterSpec.builder(xmlPullParserType, "parser").addAnnotation(nonNullType).build()
+ )
+ .addExceptions(listOf(ClassName.get(IOException::class.java), xmlPullParserExceptionType))
+ .apply {
+ addStatement("int type")
+ addStatement("int depth")
+ addStatement("int innerDepth = parser.getDepth() + 1")
+ addControlFlow(
+ "while ((type = parser.next()) != \$1T.END_DOCUMENT\$W"
+ + "&& ((depth = parser.getDepth()) >= innerDepth || type != \$1T.END_TAG))",
+ xmlPullParserType
+ ) {
+ addControlFlow(
+ "if (depth > innerDepth || type != \$1T.START_TAG)", xmlPullParserType
+ ) {
+ addStatement("continue")
+ }
+ addControlFlow(
+ "if (\$1T.equals(parser.getName(),\$W\$2S))", Objects::class.java,
+ rootField.tagName
+ ) {
+ addStatement("return \$1L(parser)", rootField.parseMethodName)
+ }
+ }
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Missing root tag <${rootField.tagName}>"
+ )
+ }
+ .build()
+
+private fun generateParseClassMethod(classField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder(classField.parseMethodName)
+ .addAnnotation(nonNullType)
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .returns(classField.type)
+ .addParameter(
+ ParameterSpec.builder(xmlPullParserType, "parser").addAnnotation(nonNullType).build()
+ )
+ .apply {
+ val (attributeFields, tagFields) = classField.fields
+ .partition { it is PrimitiveFieldInfo || it is StringFieldInfo }
+ if (tagFields.isNotEmpty()) {
+ addExceptions(
+ listOf(ClassName.get(IOException::class.java), xmlPullParserExceptionType)
+ )
+ }
+ val nameAllocator = NameAllocator().apply {
+ newName("parser")
+ newName("type")
+ newName("depth")
+ newName("innerDepth")
+ }
+ for (field in attributeFields) {
+ val variableName = nameAllocator.newName(field.variableName, field)
+ when (field) {
+ is PrimitiveFieldInfo -> {
+ val stringVariableName =
+ nameAllocator.newName("${field.variableName}String")
+ addStatement(
+ "final String \$1L =\$Wparser.getAttributeValue(null,\$W\$2S)",
+ stringVariableName, field.attributeName
+ )
+ if (field.isRequired) {
+ addControlFlow("if (\$1L == null)", stringVariableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Missing attribute \"${field.attributeName}\""
+ )
+ }
+ }
+ val boxedType = field.type.box()
+ val parseTypeMethodName = if (field.type.isPrimitive) {
+ "parse${field.type.toString().capitalize()}"
+ } else {
+ "valueOf"
+ }
+ if (field.isRequired) {
+ addStatement(
+ "final \$1T \$2L =\$W\$3T.\$4L($5L)", field.type, variableName,
+ boxedType, parseTypeMethodName, stringVariableName
+ )
+ } else {
+ addStatement(
+ "final \$1T \$2L =\$W$3L != null ?\$W\$4T.\$5L($3L)\$W: null",
+ field.type, variableName, stringVariableName, boxedType,
+ parseTypeMethodName
+ )
+ }
+ }
+ is StringFieldInfo ->
+ addStatement(
+ "final String \$1L =\$Wparser.getAttributeValue(null,\$W\$2S)",
+ variableName, field.attributeName
+ )
+ else -> error(field)
+ }
+ }
+ if (tagFields.isNotEmpty()) {
+ for (field in tagFields) {
+ val variableName = nameAllocator.newName(field.variableName, field)
+ when (field) {
+ is ClassFieldInfo ->
+ addStatement("\$1T \$2L =\$Wnull", field.type, variableName)
+ is ListFieldInfo ->
+ addStatement(
+ "final \$1T \$2L =\$Wnew \$3T<>()", field.type, variableName,
+ ArrayList::class.java
+ )
+ else -> error(field)
+ }
+ }
+ addStatement("int type")
+ addStatement("int depth")
+ addStatement("int innerDepth = parser.getDepth() + 1")
+ addControlFlow(
+ "while ((type = parser.next()) != \$1T.END_DOCUMENT\$W"
+ + "&& ((depth = parser.getDepth()) >= innerDepth || type != \$1T.END_TAG))",
+ xmlPullParserType
+ ) {
+ addControlFlow(
+ "if (depth > innerDepth || type != \$1T.START_TAG)", xmlPullParserType
+ ) {
+ addStatement("continue")
+ }
+ addControlFlow("switch (parser.getName())") {
+ for (field in tagFields) {
+ addControlFlow("case \$1S:", field.tagName) {
+ val variableName = nameAllocator.get(field)
+ when (field) {
+ is ClassFieldInfo -> {
+ addControlFlow("if (\$1L != null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Duplicate tag \"${field.tagName}\""
+ )
+ }
+ addStatement(
+ "\$1L =\$W\$2L(parser)", variableName,
+ field.parseMethodName
+ )
+ addStatement("break")
+ }
+ is ListFieldInfo -> {
+ val elementNameAllocator = nameAllocator.clone()
+ val elementVariableName = elementNameAllocator.newName(
+ field.element.xmlName!!.toLowerCamelCase()
+ )
+ addStatement(
+ "final \$1T \$2L =\$W\$3L(parser)", field.element.type,
+ elementVariableName, field.element.parseMethodName
+ )
+ addStatement(
+ "\$1L.add(\$2L)", variableName, elementVariableName
+ )
+ addStatement("break")
+ }
+ else -> error(field)
+ }
+ }
+ }
+ }
+ }
+ }
+ for (field in tagFields.filter { it is ClassFieldInfo && it.isRequired }) {
+ addControlFlow("if ($1L == null)", nameAllocator.get(field)) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)", "Missing tag <${field.tagName}>"
+ )
+ }
+ }
+ addStatement(
+ classField.fields.joinToString(",\$W", "return new \$1T(", ")") {
+ nameAllocator.get(it)
+ }, classField.type
+ )
+ }
+ .build()
+
+private val ClassFieldInfo.parseMethodName: String
+ get() = "parse${type.simpleName().toUpperCamelCase()}"
+
+private val xmlSerializerType = ClassName.get("org.xmlpull.v1", "XmlSerializer")
+
+private fun generateWriteMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("write")
+ .apply {
+ val nameAllocator = NameAllocator().apply {
+ newName("outputStream")
+ newName("serializer")
+ }
+ val parameterName = nameAllocator.newName(rootField.variableName)
+ addJavadoc(
+ """
+ Write${'$'}W{@link $1T}${'$'}Wto${'$'}Wthe${'$'}WXML${'$'}Wfile.
+
+ @param $2L the${'$'}W{@link ${'$'}1T}${'$'}Wto${'$'}Wpersist
+ """.trimIndent(), rootField.type, parameterName
+ )
+ addAnnotation(nullableType)
+ addModifiers(Modifier.PUBLIC)
+ addParameter(
+ ParameterSpec.builder(rootField.type, parameterName)
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ addStatement("\$1T outputStream = null", FileOutputStream::class.java)
+ addControlFlow("try") {
+ addStatement("outputStream = mFile.startWrite()")
+ addStatement(
+ "final \$1T serializer =\$W\$2T.newSerializer()", xmlSerializerType, xmlType
+ )
+ addStatement(
+ "serializer.setOutput(outputStream, \$1T.UTF_8.name())",
+ StandardCharsets::class.java
+ )
+ addStatement(
+ "serializer.setFeature(\$1S, true)",
+ "http://xmlpull.org/v1/doc/features.html#indent-output"
+ )
+ addStatement("serializer.startDocument(null, true)")
+ addStatement("serialize(serializer,\$W\$1L)", parameterName)
+ addStatement("serializer.endDocument()")
+ addStatement("mFile.finishWrite(outputStream)")
+ nextControlFlow("catch (Exception e)")
+ addStatement("e.printStackTrace()")
+ addStatement("mFile.failWrite(outputStream)")
+ }
+ }
+ .build()
+
+private fun generateSerializeMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("serialize")
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .addParameter(
+ ParameterSpec.builder(xmlSerializerType, "serializer")
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ .apply {
+ val nameAllocator = NameAllocator().apply { newName("serializer") }
+ val parameterName = nameAllocator.newName(rootField.variableName)
+ addParameter(
+ ParameterSpec.builder(rootField.type, parameterName)
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ addException(IOException::class.java)
+ addStatement("serializer.startTag(null, \$1S)", rootField.tagName)
+ addStatement("\$1L(serializer, \$2L)", rootField.serializeMethodName, parameterName)
+ addStatement("serializer.endTag(null, \$1S)", rootField.tagName)
+ }
+ .build()
+
+private fun generateSerializeClassMethod(classField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder(classField.serializeMethodName)
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .addParameter(
+ ParameterSpec.builder(xmlSerializerType, "serializer")
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ .apply {
+ val nameAllocator = NameAllocator().apply {
+ newName("serializer")
+ newName("i")
+ }
+ val parameterName = nameAllocator.newName(classField.serializeParameterName)
+ addParameter(
+ ParameterSpec.builder(classField.type, parameterName)
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ addException(IOException::class.java)
+ val (attributeFields, tagFields) = classField.fields
+ .partition { it is PrimitiveFieldInfo || it is StringFieldInfo }
+ for (field in attributeFields) {
+ val variableName = "$parameterName.${field.name}"
+ if (!field.isRequired) {
+ beginControlFlow("if (\$1L != null)", variableName)
+ }
+ when (field) {
+ is PrimitiveFieldInfo -> {
+ if (field.isRequired && !field.type.isPrimitive) {
+ addControlFlow("if (\$1L == null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Field \"${field.name}\" is null"
+ )
+ }
+ }
+ val stringVariableName =
+ nameAllocator.newName("${field.variableName}String")
+ addStatement(
+ "final String \$1L =\$WString.valueOf(\$2L)", stringVariableName,
+ variableName
+ )
+ addStatement(
+ "serializer.attribute(null, \$1S, \$2L)", field.attributeName,
+ stringVariableName
+ )
+ }
+ is StringFieldInfo -> {
+ if (field.isRequired) {
+ addControlFlow("if (\$1L == null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Field \"${field.name}\" is null"
+ )
+ }
+ }
+ addStatement(
+ "serializer.attribute(null, \$1S, \$2L)", field.attributeName,
+ variableName
+ )
+ }
+ else -> error(field)
+ }
+ if (!field.isRequired) {
+ endControlFlow()
+ }
+ }
+ for (field in tagFields) {
+ val variableName = "$parameterName.${field.name}"
+ if (field.isRequired) {
+ addControlFlow("if (\$1L == null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Field \"${field.name}\" is null"
+ )
+ }
+ }
+ when (field) {
+ is ClassFieldInfo -> {
+ addStatement("serializer.startTag(null, \$1S)", field.tagName)
+ addStatement(
+ "\$1L(serializer, \$2L)", field.serializeMethodName, variableName
+ )
+ addStatement("serializer.endTag(null, \$1S)", field.tagName)
+ }
+ is ListFieldInfo -> {
+ val sizeVariableName = nameAllocator.newName("${field.variableName}Size")
+ addStatement(
+ "final int \$1L =\$W\$2L.size()", sizeVariableName, variableName
+ )
+ addControlFlow("for (int i = 0;\$Wi < \$1L;\$Wi++)", sizeVariableName) {
+ val elementNameAllocator = nameAllocator.clone()
+ val elementVariableName = elementNameAllocator.newName(
+ field.element.xmlName!!.toLowerCamelCase()
+ )
+ addStatement(
+ "final \$1T \$2L =\$W\$3L.get(i)", field.element.type,
+ elementVariableName, variableName
+ )
+ addControlFlow("if (\$1L == null)", elementVariableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S\$W+ i\$W+ \$2S)",
+ "Field element \"${field.name}[", "]\" is null"
+ )
+ }
+ addStatement("serializer.startTag(null, \$1S)", field.element.tagName)
+ addStatement(
+ "\$1L(serializer,\$W\$2L)", field.element.serializeMethodName,
+ elementVariableName
+ )
+ addStatement("serializer.endTag(null, \$1S)", field.element.tagName)
+ }
+ }
+ else -> error(field)
+ }
+ }
+ }
+ .build()
+
+private val ClassFieldInfo.serializeMethodName: String
+ get() = "serialize${type.simpleName().toUpperCamelCase()}"
+
+private val ClassFieldInfo.serializeParameterName: String
+ get() = type.simpleName().toLowerCamelCase()
+
+private val FieldInfo.variableName: String
+ get() = name.toLowerCamelCase()
+
+private val FieldInfo.attributeName: String
+ get() {
+ check(this is PrimitiveFieldInfo || this is StringFieldInfo)
+ return xmlNameOrName.toLowerCamelCase()
+ }
+
+private val FieldInfo.tagName: String
+ get() {
+ check(this is ClassFieldInfo || this is ListFieldInfo)
+ return xmlNameOrName.toLowerKebabCase()
+ }
+
+private val FieldInfo.xmlNameOrName: String
+ get() = xmlName ?: name
+
+private fun generateDeleteMethod(): MethodSpec =
+ MethodSpec.methodBuilder("delete")
+ .addJavadoc("Delete the XML file, if any.")
+ .addModifiers(Modifier.PUBLIC)
+ .addStatement("mFile.delete()")
+ .build()
+
+private inline fun MethodSpec.Builder.addControlFlow(
+ controlFlow: String,
+ vararg args: Any,
+ block: MethodSpec.Builder.() -> Unit
+): MethodSpec.Builder {
+ beginControlFlow(controlFlow, *args)
+ block()
+ endControlFlow()
+ return this
+}
diff --git a/tools/xmlpersistence/src/main/kotlin/Main.kt b/tools/xmlpersistence/src/main/kotlin/Main.kt
new file mode 100644
index 000000000000..e271f8cb9361
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/Main.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+import java.io.File
+import java.nio.file.Files
+
+fun main(args: Array<String>) {
+ val showUsage = args.isEmpty() || when (args.singleOrNull()) {
+ "-h", "--help" -> true
+ else -> false
+ }
+ if (showUsage) {
+ usage()
+ return
+ }
+
+ val files = args.flatMap {
+ File(it).walk().filter { it.isFile && it.extension == "java" }.map { it.toPath() }
+ }
+ val persistences = parse(files)
+ for (persistence in persistences) {
+ val file = generate(persistence)
+ Files.newBufferedWriter(persistence.path).use {
+ it.write(FILE_HEADER)
+ file.writeTo(it)
+ }
+ }
+}
+
+private fun usage() {
+ println("Usage: xmlpersistence <FILES>")
+}
diff --git a/tools/xmlpersistence/src/main/kotlin/Parser.kt b/tools/xmlpersistence/src/main/kotlin/Parser.kt
new file mode 100644
index 000000000000..3ea12a9aa389
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/Parser.kt
@@ -0,0 +1,248 @@
+/*
+ * 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.
+ */
+
+import com.github.javaparser.JavaParser
+import com.github.javaparser.ParseProblemException
+import com.github.javaparser.ParseResult
+import com.github.javaparser.ParserConfiguration
+import com.github.javaparser.ast.Node
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
+import com.github.javaparser.ast.body.FieldDeclaration
+import com.github.javaparser.ast.body.TypeDeclaration
+import com.github.javaparser.ast.expr.AnnotationExpr
+import com.github.javaparser.ast.expr.Expression
+import com.github.javaparser.ast.expr.NormalAnnotationExpr
+import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr
+import com.github.javaparser.ast.expr.StringLiteralExpr
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration
+import com.github.javaparser.resolution.types.ResolvedPrimitiveType
+import com.github.javaparser.resolution.types.ResolvedReferenceType
+import com.github.javaparser.symbolsolver.JavaSymbolSolver
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserClassDeclaration
+import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver
+import com.github.javaparser.symbolsolver.resolution.typesolvers.MemoryTypeSolver
+import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import java.nio.file.Path
+import java.util.Optional
+
+class PersistenceInfo(
+ val name: String,
+ val root: ClassFieldInfo,
+ val path: Path
+)
+
+sealed class FieldInfo {
+ abstract val name: String
+ abstract val xmlName: String?
+ abstract val type: TypeName
+ abstract val isRequired: Boolean
+}
+
+class PrimitiveFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val type: TypeName,
+ override val isRequired: Boolean
+) : FieldInfo()
+
+class StringFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val isRequired: Boolean
+) : FieldInfo() {
+ override val type: TypeName = ClassName.get(String::class.java)
+}
+
+class ClassFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val type: ClassName,
+ override val isRequired: Boolean,
+ val fields: List<FieldInfo>
+) : FieldInfo()
+
+class ListFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val type: ParameterizedTypeName,
+ val element: ClassFieldInfo
+) : FieldInfo() {
+ override val isRequired: Boolean = true
+}
+
+fun parse(files: List<Path>): List<PersistenceInfo> {
+ val typeSolver = CombinedTypeSolver().apply { add(ReflectionTypeSolver()) }
+ val javaParser = JavaParser(ParserConfiguration()
+ .setSymbolResolver(JavaSymbolSolver(typeSolver)))
+ val compilationUnits = files.map { javaParser.parse(it).getOrThrow() }
+ val memoryTypeSolver = MemoryTypeSolver().apply {
+ for (compilationUnit in compilationUnits) {
+ for (typeDeclaration in compilationUnit.getNodesByClass<TypeDeclaration<*>>()) {
+ val name = typeDeclaration.fullyQualifiedName.getOrNull() ?: continue
+ addDeclaration(name, typeDeclaration.resolve())
+ }
+ }
+ }
+ typeSolver.add(memoryTypeSolver)
+ return mutableListOf<PersistenceInfo>().apply {
+ for (compilationUnit in compilationUnits) {
+ val classDeclarations = compilationUnit
+ .getNodesByClass<ClassOrInterfaceDeclaration>()
+ .filter { !it.isInterface && (!it.isNestedType || it.isStatic) }
+ this += classDeclarations.mapNotNull { parsePersistenceInfo(it) }
+ }
+ }
+}
+
+private fun parsePersistenceInfo(classDeclaration: ClassOrInterfaceDeclaration): PersistenceInfo? {
+ val annotation = classDeclaration.getAnnotationByName("XmlPersistence").getOrNull()
+ ?: return null
+ val rootClassName = classDeclaration.nameAsString
+ val name = annotation.getMemberValue("value")?.stringLiteralValue
+ ?: "${rootClassName}Persistence"
+ val rootXmlName = classDeclaration.getAnnotationByName("XmlName").getOrNull()
+ ?.getMemberValue("value")?.stringLiteralValue
+ val root = parseClassFieldInfo(
+ rootXmlName ?: rootClassName, rootXmlName, true, classDeclaration
+ )
+ val path = classDeclaration.findCompilationUnit().get().storage.get().path
+ .resolveSibling("$name.java")
+ return PersistenceInfo(name, root, path)
+}
+
+private fun parseClassFieldInfo(
+ name: String,
+ xmlName: String?,
+ isRequired: Boolean,
+ classDeclaration: ClassOrInterfaceDeclaration
+): ClassFieldInfo {
+ val fields = classDeclaration.fields.filterNot { it.isStatic }.map { parseFieldInfo(it) }
+ val type = classDeclaration.resolve().typeName
+ return ClassFieldInfo(name, xmlName, type, isRequired, fields)
+}
+
+private fun parseFieldInfo(field: FieldDeclaration): FieldInfo {
+ require(field.isPublic && field.isFinal)
+ val variable = field.variables.single()
+ val name = variable.nameAsString
+ val annotations = field.annotations + variable.type.annotations
+ val annotation = annotations.getByName("XmlName")
+ val xmlName = annotation?.getMemberValue("value")?.stringLiteralValue
+ val isRequired = annotations.getByName("NonNull") != null
+ return when (val type = variable.type.resolve()) {
+ is ResolvedPrimitiveType -> {
+ val primitiveType = type.typeName
+ PrimitiveFieldInfo(name, xmlName, primitiveType, true)
+ }
+ is ResolvedReferenceType -> {
+ when (type.qualifiedName) {
+ Boolean::class.javaObjectType.name, Byte::class.javaObjectType.name,
+ Short::class.javaObjectType.name, Char::class.javaObjectType.name,
+ Integer::class.javaObjectType.name, Long::class.javaObjectType.name,
+ Float::class.javaObjectType.name, Double::class.javaObjectType.name ->
+ PrimitiveFieldInfo(name, xmlName, type.typeName, isRequired)
+ String::class.java.name -> StringFieldInfo(name, xmlName, isRequired)
+ List::class.java.name -> {
+ requireNotNull(xmlName)
+ val elementType = type.typeParametersValues().single()
+ require(elementType is ResolvedReferenceType)
+ val listType = ParameterizedTypeName.get(
+ ClassName.get(List::class.java), elementType.typeName
+ )
+ val element = parseClassFieldInfo(
+ "(element)", xmlName, true, elementType.classDeclaration
+ )
+ ListFieldInfo(name, xmlName, listType, element)
+ }
+ else -> parseClassFieldInfo(name, xmlName, isRequired, type.classDeclaration)
+ }
+ }
+ else -> error(type)
+ }
+}
+
+private fun <T> ParseResult<T>.getOrThrow(): T =
+ if (isSuccessful) {
+ result.get()
+ } else {
+ throw ParseProblemException(problems)
+ }
+
+private inline fun <reified T : Node> Node.getNodesByClass(): List<T> =
+ getNodesByClass(T::class.java)
+
+private fun <T : Node> Node.getNodesByClass(klass: Class<T>): List<T> = mutableListOf<T>().apply {
+ if (klass.isInstance(this@getNodesByClass)) {
+ this += klass.cast(this@getNodesByClass)
+ }
+ for (childNode in childNodes) {
+ this += childNode.getNodesByClass(klass)
+ }
+}
+
+private fun <T> Optional<T>.getOrNull(): T? = orElse(null)
+
+private fun List<AnnotationExpr>.getByName(name: String): AnnotationExpr? =
+ find { it.name.identifier == name }
+
+private fun AnnotationExpr.getMemberValue(name: String): Expression? =
+ when (this) {
+ is NormalAnnotationExpr -> pairs.find { it.nameAsString == name }?.value
+ is SingleMemberAnnotationExpr -> if (name == "value") memberValue else null
+ else -> null
+ }
+
+private val Expression.stringLiteralValue: String
+ get() {
+ require(this is StringLiteralExpr)
+ return value
+ }
+
+private val ResolvedReferenceType.classDeclaration: ClassOrInterfaceDeclaration
+ get() {
+ val resolvedClassDeclaration = typeDeclaration
+ require(resolvedClassDeclaration is JavaParserClassDeclaration)
+ return resolvedClassDeclaration.wrappedNode
+ }
+
+private val ResolvedPrimitiveType.typeName: TypeName
+ get() =
+ when (this) {
+ ResolvedPrimitiveType.BOOLEAN -> TypeName.BOOLEAN
+ ResolvedPrimitiveType.BYTE -> TypeName.BYTE
+ ResolvedPrimitiveType.SHORT -> TypeName.SHORT
+ ResolvedPrimitiveType.CHAR -> TypeName.CHAR
+ ResolvedPrimitiveType.INT -> TypeName.INT
+ ResolvedPrimitiveType.LONG -> TypeName.LONG
+ ResolvedPrimitiveType.FLOAT -> TypeName.FLOAT
+ ResolvedPrimitiveType.DOUBLE -> TypeName.DOUBLE
+ }
+
+// This doesn't support type parameters.
+private val ResolvedReferenceType.typeName: TypeName
+ get() = typeDeclaration.typeName
+
+private val ResolvedReferenceTypeDeclaration.typeName: ClassName
+ get() {
+ val packageName = packageName
+ val classNames = className.split(".")
+ val topLevelClassName = classNames.first()
+ val nestedClassNames = classNames.drop(1)
+ return ClassName.get(packageName, topLevelClassName, *nestedClassNames.toTypedArray())
+ }
diff --git a/tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt b/tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt
new file mode 100644
index 000000000000..b4bdbba7170b
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Locale
+
+private val camelHumpBoundary = Regex(
+ "-"
+ + "|_"
+ + "|(?<=[0-9])(?=[^0-9])"
+ + "|(?<=[A-Z])(?=[^A-Za-z]|[A-Z][a-z])"
+ + "|(?<=[a-z])(?=[^a-z])"
+)
+
+private fun String.toCamelHumps(): List<String> = split(camelHumpBoundary)
+
+fun String.toUpperCamelCase(): String =
+ toCamelHumps().joinToString("") { it.toLowerCase(Locale.ROOT).capitalize(Locale.ROOT) }
+
+fun String.toLowerCamelCase(): String = toUpperCamelCase().decapitalize(Locale.ROOT)
+
+fun String.toUpperKebabCase(): String =
+ toCamelHumps().joinToString("-") { it.toUpperCase(Locale.ROOT) }
+
+fun String.toLowerKebabCase(): String =
+ toCamelHumps().joinToString("-") { it.toLowerCase(Locale.ROOT) }
+
+fun String.toUpperSnakeCase(): String =
+ toCamelHumps().joinToString("_") { it.toUpperCase(Locale.ROOT) }
+
+fun String.toLowerSnakeCase(): String =
+ toCamelHumps().joinToString("_") { it.toLowerCase(Locale.ROOT) }
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index fd45ebec80ef..0f71ab46dbaf 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -509,7 +509,7 @@ package android.net.wifi {
field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
field public static final String EXTRA_CHANGE_REASON = "changeReason";
field @Deprecated public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
- field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
+ field @Deprecated public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK";
field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
field public static final String EXTRA_URL = "android.net.wifi.extra.URL";
@@ -517,7 +517,7 @@ package android.net.wifi {
field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME";
field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE";
field public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
- field public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
+ field @Deprecated public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
field public static final String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et";
field public static final String EXTRA_WIFI_CREDENTIAL_SSID = "ssid";
field public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0; // 0x0
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 391be357e963..0973d545fd37 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -29,7 +29,6 @@ import android.net.NetworkSpecifier;
import android.net.ProxyInfo;
import android.net.StaticIpConfiguration;
import android.net.Uri;
-import android.net.util.MacAddressUtils;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -41,6 +40,7 @@ import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.MacAddressUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 2219bfcd6aab..b4e42108a15f 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -931,9 +931,6 @@ public class WifiManager {
* This can be as a result of adding/updating/deleting a network.
* <br />
* {@link #EXTRA_CHANGE_REASON} contains whether the configuration was added/changed/removed.
- * {@link #EXTRA_WIFI_CONFIGURATION} is never set starting in Android 11.
- * {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is set for backwards compatibility reasons, but
- * its value is always true, even if only a single network changed.
* <br />
* The {@link android.Manifest.permission#ACCESS_WIFI_STATE ACCESS_WIFI_STATE} permission is
* required to receive this broadcast.
@@ -947,17 +944,20 @@ public class WifiManager {
* The lookup key for a {@link android.net.wifi.WifiConfiguration} object representing
* the changed Wi-Fi configuration when the {@link #CONFIGURED_NETWORKS_CHANGED_ACTION}
* broadcast is sent.
- * Note: this extra is never set starting in Android 11.
+ * @deprecated this extra is never set. Use {@link #getConfiguredNetworks} to get the full list
+ * of configured networks.
* @hide
*/
+ @Deprecated
@SystemApi
public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
/**
* Multiple network configurations have changed.
* @see #CONFIGURED_NETWORKS_CHANGED_ACTION
- * Note: this extra is always true starting in Android 11.
+ * @deprecated this extra's value is always true.
* @hide
*/
+ @Deprecated
@SystemApi
public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
/**
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
index dad431c1ca2c..e2f40cfa058c 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
@@ -17,10 +17,11 @@
package android.net.wifi.p2p.nsd;
import android.compat.annotation.UnsupportedAppUsage;
-import android.net.util.nsd.DnsSdTxtRecord;
import android.os.Build;
import android.text.TextUtils;
+import com.android.net.module.util.DnsSdTxtRecord;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
diff --git a/wifi/java/android/net/wifi/util/SdkLevelUtil.java b/wifi/java/android/net/wifi/util/SdkLevelUtil.java
index 042634c7125c..d08d4fd742b7 100644
--- a/wifi/java/android/net/wifi/util/SdkLevelUtil.java
+++ b/wifi/java/android/net/wifi/util/SdkLevelUtil.java
@@ -23,17 +23,17 @@ import android.os.Build;
*
* This can be used to disable new Wifi APIs added in Mainline updates on older SDK versions.
*
+ * Note: if certain functionality is gated with SdkLevelUtil, its corresponding unit tests should
+ * also be gated by the same condition. Then, those unit tests will only be exercised on a base
+ * system image satisfying that condition.
+ * Alternatively, it can be tested via static mocking.
+ *
* @hide
*/
public class SdkLevelUtil {
- /** This class is instantiable to allow easy mocking. */
- public SdkLevelUtil() { }
-
- /** See {@link #isAtLeastS()}. This version is non-static to allow easy mocking. */
- public boolean isAtLeastSMockable() {
- return isAtLeastS();
- }
+ /** This class is not instantiable. */
+ private SdkLevelUtil() {}
/** Returns true if the Android platform SDK is at least "S", false otherwise. */
public static boolean isAtLeastS() {
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 62220a6237b1..d4b20519215c 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -33,13 +33,14 @@ import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import android.net.MacAddress;
-import android.net.util.MacAddressUtils;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
+import com.android.net.module.util.MacAddressUtils;
+
import org.junit.Before;
import org.junit.Test;