summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp31
-rw-r--r--StubLibraries.bp62
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java20
-rw-r--r--apex/media/service/Android.bp3
-rw-r--r--apex/media/service/jarjar_rules.txt1
-rw-r--r--apex/media/service/java/com/android/server/media/MediaCommunicationService.java3
-rw-r--r--core/api/current.txt87
-rw-r--r--core/api/module-lib-current.txt7
-rw-r--r--core/api/system-current.txt187
-rw-r--r--core/api/test-current.txt65
-rw-r--r--core/java/android/app/ActivityClient.java23
-rw-r--r--core/java/android/app/ActivityThread.java42
-rw-r--r--core/java/android/app/IActivityClientController.aidl12
-rw-r--r--core/java/android/app/ICompatCameraControlCallback.aidl (renamed from media/java/android/media/tv/BroadcastInfoType.java)24
-rw-r--r--core/java/android/app/Instrumentation.java2
-rw-r--r--core/java/android/app/SystemServiceRegistry.java18
-rw-r--r--core/java/android/app/TaskInfo.java88
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java104
-rw-r--r--core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java34
-rw-r--r--core/java/android/app/admin/ManagedProfileProvisioningParams.java56
-rw-r--r--core/java/android/app/admin/ProvisioningException.java90
-rw-r--r--core/java/android/app/backup/BackupTransport.java159
-rw-r--r--core/java/android/app/cloudsearch/OWNERS4
-rw-r--r--core/java/android/app/compat/ChangeIdStateQuery.java10
-rw-r--r--core/java/android/app/prediction/AppPredictor.java2
-rw-r--r--core/java/android/app/search/SearchSession.java2
-rw-r--r--core/java/android/app/smartspace/SmartspaceSession.java2
-rw-r--r--core/java/android/bluetooth/BluetoothA2dp.java511
-rwxr-xr-xcore/java/android/bluetooth/BluetoothA2dpSink.java183
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java2
-rw-r--r--core/java/android/bluetooth/BluetoothAvrcpController.java135
-rw-r--r--core/java/android/bluetooth/BluetoothCsipSetCoordinator.java260
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java2
-rw-r--r--core/java/android/bluetooth/BluetoothGatt.java10
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java522
-rw-r--r--core/java/android/bluetooth/BluetoothHeadsetClient.java581
-rw-r--r--core/java/android/bluetooth/BluetoothHearingAid.java302
-rw-r--r--core/java/android/bluetooth/BluetoothHidDevice.java264
-rw-r--r--core/java/android/bluetooth/BluetoothHidHost.java291
-rw-r--r--core/java/android/bluetooth/BluetoothLeAudio.java307
-rw-r--r--core/java/android/bluetooth/BluetoothManager.java34
-rw-r--r--core/java/android/bluetooth/BluetoothMap.java185
-rw-r--r--core/java/android/bluetooth/BluetoothMapClient.java251
-rw-r--r--core/java/android/bluetooth/BluetoothPan.java159
-rw-r--r--core/java/android/bluetooth/BluetoothPbap.java135
-rw-r--r--core/java/android/bluetooth/BluetoothPbapClient.java157
-rw-r--r--core/java/android/bluetooth/BluetoothProfileConnector.java32
-rw-r--r--core/java/android/bluetooth/BluetoothSap.java184
-rw-r--r--core/java/android/bluetooth/BluetoothUtils.java41
-rw-r--r--core/java/android/bluetooth/BluetoothVolumeControl.java128
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeScanner.java13
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java106
-rw-r--r--core/java/android/content/AttributionSource.java43
-rw-r--r--core/java/android/content/BroadcastReceiver.java2
-rw-r--r--core/java/android/content/ContentProviderClient.java4
-rw-r--r--core/java/android/content/ContentResolver.java2
-rw-r--r--core/java/android/content/Context.java9
-rw-r--r--core/java/android/content/Intent.java30
-rw-r--r--core/java/android/content/pm/PackageManager.java73
-rw-r--r--core/java/android/content/res/Configuration.java47
-rw-r--r--core/java/android/database/AbstractCursor.java2
-rw-r--r--core/java/android/database/CursorWindow.java4
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java2
-rw-r--r--core/java/android/database/sqlite/SQLiteConnectionPool.java2
-rw-r--r--core/java/android/hardware/HardwareBuffer.java2
-rw-r--r--core/java/android/hardware/SystemSensorManager.java2
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl2
-rw-r--r--core/java/android/hardware/input/InputDeviceLightsManager.java2
-rw-r--r--core/java/android/hardware/input/InputManager.java12
-rw-r--r--core/java/android/hardware/lights/SystemLightsManager.java2
-rw-r--r--core/java/android/hardware/location/ContextHubClient.java2
-rw-r--r--core/java/android/hardware/usb/UsbDeviceConnection.java2
-rw-r--r--core/java/android/hardware/usb/UsbRequest.java2
-rw-r--r--core/java/android/net/NetworkPolicy.java29
-rw-r--r--core/java/android/os/BatteryConsumer.java26
-rw-r--r--core/java/android/os/BatteryStats.java19
-rw-r--r--core/java/android/os/BatteryUsageStats.java2
-rw-r--r--core/java/android/os/Binder.java7
-rw-r--r--core/java/android/os/PowerComponents.java5
-rw-r--r--core/java/android/os/SystemVibrator.java2
-rw-r--r--core/java/android/os/UserManager.java11
-rw-r--r--core/java/android/os/storage/StorageManager.java83
-rw-r--r--core/java/android/provider/Settings.java20
-rw-r--r--core/java/android/provider/Telephony.java14
-rw-r--r--core/java/android/security/keymaster/KeymasterDefs.java19
-rw-r--r--core/java/android/service/cloudsearch/OWNERS4
-rw-r--r--core/java/android/service/dreams/DreamService.java2
-rw-r--r--core/java/android/service/games/CreateGameSessionRequest.aidl23
-rw-r--r--core/java/android/service/games/CreateGameSessionRequest.java118
-rw-r--r--core/java/android/service/games/GameService.java18
-rw-r--r--core/java/android/service/games/GameSession.java65
-rw-r--r--core/java/android/service/games/GameSessionService.java104
-rw-r--r--core/java/android/service/games/IGameSession.aidl24
-rw-r--r--core/java/android/service/games/IGameSessionService.aidl32
-rw-r--r--core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl31
-rw-r--r--core/java/android/service/selectiontoolbar/OWNERS10
-rw-r--r--core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java53
-rw-r--r--core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java182
-rw-r--r--core/java/android/telephony/PhoneStateListener.java1
-rw-r--r--core/java/android/telephony/TelephonyCallback.java1
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java146
-rw-r--r--core/java/android/util/FeatureFlagUtils.java4
-rw-r--r--core/java/android/util/MemoryIntArray.java4
-rw-r--r--core/java/android/view/InputEventReceiver.java2
-rw-r--r--core/java/android/view/InputEventSender.java2
-rw-r--r--core/java/android/view/InputQueue.java2
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java11
-rw-r--r--core/java/android/view/MotionEvent.java2
-rw-r--r--core/java/android/view/ScrollCaptureConnection.java2
-rw-r--r--core/java/android/view/Surface.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java38
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java34
-rw-r--r--core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl33
-rw-r--r--core/java/android/view/selectiontoolbar/ISelectionToolbarManager.aidl31
-rw-r--r--core/java/android/view/selectiontoolbar/OWNERS10
-rw-r--r--core/java/android/view/selectiontoolbar/SelectionContext.aidl22
-rw-r--r--core/java/android/view/selectiontoolbar/SelectionContext.java248
-rw-r--r--core/java/android/view/selectiontoolbar/SelectionToolbarManager.java89
-rw-r--r--core/java/android/view/selectiontoolbar/ShowInfo.aidl22
-rw-r--r--core/java/android/view/selectiontoolbar/ShowInfo.java169
-rw-r--r--core/java/android/view/selectiontoolbar/ToolbarMenuItem.aidl22
-rw-r--r--core/java/android/view/selectiontoolbar/ToolbarMenuItem.java211
-rw-r--r--core/java/android/view/selectiontoolbar/WidgetInfo.aidl22
-rw-r--r--core/java/android/view/selectiontoolbar/WidgetInfo.java168
-rw-r--r--core/java/android/widget/Editor.java2
-rw-r--r--core/java/android/window/ITaskOrganizerController.aidl3
-rw-r--r--core/java/android/window/TaskOrganizer.java15
-rw-r--r--core/java/com/android/internal/backup/IBackupTransport.aidl285
-rw-r--r--core/java/com/android/internal/backup/ITransportStatusCallback.aidl34
-rw-r--r--core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java134
-rw-r--r--core/java/com/android/internal/net/NetworkUtilsInternal.java31
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java189
-rw-r--r--core/java/com/android/internal/os/CpuPowerCalculator.java10
-rw-r--r--core/java/com/android/internal/os/LongMultiStateCounter.java22
-rw-r--r--core/java/com/android/internal/os/MobileRadioPowerCalculator.java82
-rw-r--r--core/java/com/android/internal/power/MeasuredEnergyStats.java4
-rw-r--r--core/java/com/android/internal/telephony/ICarrierPrivilegesListener.aidl22
-rw-r--r--core/java/com/android/internal/telephony/ITelephonyRegistry.aidl8
-rw-r--r--core/jni/android_view_InputQueue.cpp3
-rw-r--r--core/jni/com_android_internal_os_LongMultiStateCounter.cpp6
-rw-r--r--core/jni/include/android_runtime/android_view_InputQueue.h2
-rw-r--r--core/res/AndroidManifest.xml31
-rw-r--r--core/res/res/drawable/ic_add_supervised_user.xml34
-rw-r--r--core/res/res/values-af/strings.xml15
-rw-r--r--core/res/res/values-am/strings.xml15
-rw-r--r--core/res/res/values-ar/strings.xml17
-rw-r--r--core/res/res/values-as/strings.xml15
-rw-r--r--core/res/res/values-az/strings.xml15
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml15
-rw-r--r--core/res/res/values-be/strings.xml21
-rw-r--r--core/res/res/values-bg/strings.xml15
-rw-r--r--core/res/res/values-bn/strings.xml15
-rw-r--r--core/res/res/values-bs/strings.xml17
-rw-r--r--core/res/res/values-ca/strings.xml15
-rw-r--r--core/res/res/values-cs/strings.xml15
-rw-r--r--core/res/res/values-da/strings.xml15
-rw-r--r--core/res/res/values-de/strings.xml15
-rw-r--r--core/res/res/values-el/strings.xml15
-rw-r--r--core/res/res/values-en-rAU/strings.xml15
-rw-r--r--core/res/res/values-en-rCA/strings.xml15
-rw-r--r--core/res/res/values-en-rGB/strings.xml15
-rw-r--r--core/res/res/values-en-rIN/strings.xml15
-rw-r--r--core/res/res/values-en-rXC/strings.xml15
-rw-r--r--core/res/res/values-es-rUS/strings.xml21
-rw-r--r--core/res/res/values-es/strings.xml15
-rw-r--r--core/res/res/values-et/strings.xml15
-rw-r--r--core/res/res/values-eu/strings.xml15
-rw-r--r--core/res/res/values-fa/strings.xml15
-rw-r--r--core/res/res/values-fi/strings.xml15
-rw-r--r--core/res/res/values-fr-rCA/strings.xml16
-rw-r--r--core/res/res/values-fr/strings.xml15
-rw-r--r--core/res/res/values-gl/strings.xml15
-rw-r--r--core/res/res/values-gu/strings.xml15
-rw-r--r--core/res/res/values-hi/strings.xml15
-rw-r--r--core/res/res/values-hr/strings.xml15
-rw-r--r--core/res/res/values-hu/strings.xml15
-rw-r--r--core/res/res/values-hy/strings.xml15
-rw-r--r--core/res/res/values-in/strings.xml15
-rw-r--r--core/res/res/values-is/strings.xml15
-rw-r--r--core/res/res/values-it/strings.xml15
-rw-r--r--core/res/res/values-iw/strings.xml15
-rw-r--r--core/res/res/values-ja/strings.xml15
-rw-r--r--core/res/res/values-ka/strings.xml15
-rw-r--r--core/res/res/values-kk/strings.xml15
-rw-r--r--core/res/res/values-km/strings.xml15
-rw-r--r--core/res/res/values-kn/strings.xml15
-rw-r--r--core/res/res/values-ko/strings.xml15
-rw-r--r--core/res/res/values-ky/strings.xml15
-rw-r--r--core/res/res/values-lo/strings.xml15
-rw-r--r--core/res/res/values-lt/strings.xml15
-rw-r--r--core/res/res/values-lv/strings.xml19
-rw-r--r--core/res/res/values-mk/strings.xml15
-rw-r--r--core/res/res/values-ml/strings.xml15
-rw-r--r--core/res/res/values-mn/strings.xml15
-rw-r--r--core/res/res/values-mr/strings.xml15
-rw-r--r--core/res/res/values-ms/strings.xml15
-rw-r--r--core/res/res/values-my/strings.xml21
-rw-r--r--core/res/res/values-nb/strings.xml15
-rw-r--r--core/res/res/values-ne/strings.xml15
-rw-r--r--core/res/res/values-nl/strings.xml15
-rw-r--r--core/res/res/values-or/strings.xml15
-rw-r--r--core/res/res/values-pa/strings.xml15
-rw-r--r--core/res/res/values-pl/strings.xml15
-rw-r--r--core/res/res/values-pt-rBR/strings.xml23
-rw-r--r--core/res/res/values-pt-rPT/strings.xml15
-rw-r--r--core/res/res/values-pt/strings.xml23
-rw-r--r--core/res/res/values-ro/strings.xml15
-rw-r--r--core/res/res/values-ru/strings.xml15
-rw-r--r--core/res/res/values-si/strings.xml15
-rw-r--r--core/res/res/values-sk/strings.xml15
-rw-r--r--core/res/res/values-sl/strings.xml15
-rw-r--r--core/res/res/values-sq/strings.xml15
-rw-r--r--core/res/res/values-sr/strings.xml15
-rw-r--r--core/res/res/values-sv/strings.xml15
-rw-r--r--core/res/res/values-sw/strings.xml15
-rw-r--r--core/res/res/values-ta/strings.xml15
-rw-r--r--core/res/res/values-te/strings.xml21
-rw-r--r--core/res/res/values-th/strings.xml15
-rw-r--r--core/res/res/values-tl/strings.xml15
-rw-r--r--core/res/res/values-tr/strings.xml15
-rw-r--r--core/res/res/values-uk/strings.xml15
-rw-r--r--core/res/res/values-ur/strings.xml15
-rw-r--r--core/res/res/values-uz/strings.xml15
-rw-r--r--core/res/res/values-vi/strings.xml15
-rw-r--r--core/res/res/values-zh-rCN/strings.xml15
-rw-r--r--core/res/res/values-zh-rHK/strings.xml15
-rw-r--r--core/res/res/values-zh-rTW/strings.xml15
-rw-r--r--core/res/res/values-zu/strings.xml15
-rw-r--r--core/res/res/values/attrs.xml11
-rw-r--r--core/res/res/values/config.xml30
-rw-r--r--core/res/res/values/public.xml3
-rw-r--r--core/res/res/values/strings.xml2
-rw-r--r--core/res/res/values/symbols.xml7
-rw-r--r--core/tests/coretests/Android.bp36
-rw-r--r--core/tests/coretests/src/android/net/NetworkPolicyTest.kt28
-rw-r--r--core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java49
-rw-r--r--core/tests/coretests/src/android/view/MotionEventTest.java24
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java115
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java1221
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java109
-rw-r--r--core/tests/coretests/src/com/android/internal/app/IChooserWrapper.java44
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java182
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java5
-rw-r--r--core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java19
-rw-r--r--data/etc/privapp-permissions-platform.xml3
-rw-r--r--data/etc/services.core.protolog.json6
-rw-r--r--graphics/java/android/graphics/RuntimeShader.java292
-rw-r--r--keystore/java/android/security/KeyStoreException.java433
-rw-r--r--libs/WindowManager/Shell/res/color/compat_background_ripple.xml (renamed from libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml)0
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml33
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml20
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml32
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml20
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml53
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml20
-rw-r--r--libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml4
-rw-r--r--libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/compat_mode_hint.xml4
-rw-r--r--libs/WindowManager/Shell/res/layout/compat_ui_layout.xml49
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml6
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java74
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java80
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java114
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java288
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java84
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java126
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java93
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java157
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java77
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java5
-rw-r--r--libs/hwui/jni/Shader.cpp99
-rw-r--r--libs/hwui/renderthread/EglManager.cpp14
-rw-r--r--media/java/android/media/AudioManager.java83
-rw-r--r--media/java/android/media/MediaActionSound.java25
-rw-r--r--media/java/android/media/tv/BroadcastInfoRequest.java44
-rw-r--r--media/java/android/media/tv/BroadcastInfoResponse.java47
-rw-r--r--media/java/android/media/tv/CommandRequest.java (renamed from media/java/android/media/tv/TvProprietaryFunctionRequest.java)23
-rw-r--r--media/java/android/media/tv/CommandResponse.java (renamed from media/java/android/media/tv/TvProprietaryFunctionResponse.java)25
-rw-r--r--media/java/android/media/tv/DsmccRequest.java7
-rw-r--r--media/java/android/media/tv/DsmccResponse.java17
-rw-r--r--media/java/android/media/tv/PesRequest.java7
-rw-r--r--media/java/android/media/tv/PesResponse.java8
-rw-r--r--media/java/android/media/tv/SectionRequest.java16
-rw-r--r--media/java/android/media/tv/SectionResponse.java9
-rw-r--r--media/java/android/media/tv/StreamEventRequest.java8
-rw-r--r--media/java/android/media/tv/StreamEventResponse.java9
-rw-r--r--media/java/android/media/tv/TableRequest.java25
-rw-r--r--media/java/android/media/tv/TableResponse.java9
-rw-r--r--media/java/android/media/tv/TsRequest.java7
-rw-r--r--media/java/android/media/tv/TsResponse.java8
-rw-r--r--media/java/android/media/tv/TvContract.java62
-rw-r--r--media/java/android/media/tv/TvInputManager.java22
-rw-r--r--media/java/android/media/tv/interactive/ITvIAppClient.aidl5
-rw-r--r--media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl4
-rw-r--r--media/java/android/media/tv/interactive/TvIAppManager.java34
-rw-r--r--media/java/android/media/tv/interactive/TvIAppService.java50
-rw-r--r--media/java/android/media/tv/interactive/TvIAppView.java45
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrPlayback.java8
-rw-r--r--media/java/android/media/tv/tuner/filter/DownloadSettings.java10
-rw-r--r--media/java/android/media/tv/tuner/filter/Filter.java3
-rw-r--r--media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java2
-rw-r--r--media/java/android/media/tv/tuner/filter/SectionEvent.java9
-rw-r--r--media/java/android/media/tv/tuner/filter/SectionSettings.java27
-rw-r--r--media/java/android/media/tv/tuner/filter/TsRecordEvent.java2
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendStatus.java42
-rw-r--r--media/java/android/media/tv/tuner/frontend/ScanCallback.java7
-rw-r--r--native/android/input.cpp4
-rw-r--r--packages/CompanionDeviceManager/res/values-af/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-am/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ar/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-as/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-az/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-be/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-bg/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-bn/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-bs/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ca/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-cs/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-da/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-de/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-el/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rAU/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rCA/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rGB/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rIN/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rXC/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/res/values-es-rUS/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-es/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-et/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-eu/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-fa/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-fi/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-fr/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-gl/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-gu/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-hi/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-hr/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-hu/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-hy/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-in/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-is/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-it/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-iw/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ja/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ka/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-kk/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-km/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-kn/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ko/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ky/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-lo/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-lt/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-lv/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-mk/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/res/values-ml/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-mn/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-mr/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ms/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-my/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-nb/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ne/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-nl/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-or/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-pa/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-pl/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-pt/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ro/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ru/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-si/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-sk/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-sl/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-sq/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-sr/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-sv/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-sw/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ta/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-te/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-th/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-tl/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-tr/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-uk/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ur/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-uz/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-vi/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-zu/strings.xml20
-rw-r--r--packages/ConnectivityT/framework-t/Android.bp18
-rw-r--r--packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java11
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java (renamed from core/java/android/net/EthernetManager.java)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java (renamed from core/java/android/net/EthernetNetworkSpecifier.java)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl (renamed from core/java/android/net/IEthernetManager.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IEthernetServiceListener.aidl (renamed from core/java/android/net/IEthernetServiceListener.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/ITetheredInterfaceCallback.aidl (renamed from core/java/android/net/ITetheredInterfaceCallback.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java9
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java1
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java2
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java2
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java2
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java430
-rw-r--r--packages/ConnectivityT/service/Android.bp14
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/IpSecService.java194
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/IpConfigStore.java (renamed from services/core/java/com/android/server/net/IpConfigStore.java)32
-rw-r--r--packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java29
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java2
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/Shell/AndroidManifest.xml4
-rw-r--r--packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java15
-rw-r--r--packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_header_bg.xml (renamed from packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_header_bg.xml)4
-rw-r--r--packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_item_selected_bg.xml22
-rw-r--r--packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_popup_bg.xml (renamed from packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_popup_bg.xml)2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml11
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml17
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml18
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml21
-rw-r--r--packages/SystemUI/res/drawable/ic_circle_check_box.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_circular_unchecked.xml9
-rw-r--r--packages/SystemUI/res/layout/combined_qs_header.xml12
-rw-r--r--packages/SystemUI/res/layout/internet_connectivity_dialog.xml16
-rw-r--r--packages/SystemUI/res/layout/media_output_list_item.xml30
-rw-r--r--packages/SystemUI/res/layout/media_ttt_chip.xml11
-rw-r--r--packages/SystemUI/res/values/config.xml14
-rw-r--r--packages/SystemUI/res/values/dimens.xml9
-rw-r--r--packages/SystemUI/res/xml/combined_qs_header_scene.xml13
-rw-r--r--packages/SystemUI/res/xml/qqs_header.xml19
-rw-r--r--packages/SystemUI/res/xml/qs_header.xml11
-rw-r--r--packages/SystemUI/res/xml/split_header.xml24
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java99
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java135
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt187
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeHost.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeUi.java103
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/ComplicationHost.java (renamed from packages/SystemUI/src/com/android/systemui/dreams/OverlayHost.java)20
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/ComplicationHostView.java (renamed from packages/SystemUI/src/com/android/systemui/dreams/OverlayHostView.java)13
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java (renamed from packages/SystemUI/src/com/android/systemui/dreams/OverlayProvider.java)16
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java122
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationPrimer.java (renamed from packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java)45
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationProvider.java (renamed from packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayProvider.java)25
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/appwidgets/dagger/AppWidgetComponent.java (renamed from packages/SystemUI/src/com/android/systemui/dreams/dagger/AppWidgetOverlayComponent.java)16
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/StatusBarFlags.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java83
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java88
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java124
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/AppWidgetOverlayProviderTest.java)20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationPrimerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimerTest.java)83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt134
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt155
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java193
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt5
-rw-r--r--packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml2
-rw-r--r--packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml8
-rw-r--r--packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml8
-rw-r--r--packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml8
-rw-r--r--packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml8
-rw-r--r--services/Android.bp2
-rw-r--r--services/OWNERS2
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java226
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java91
-rw-r--r--services/cloudsearch/OWNERS4
-rw-r--r--services/companion/java/com/android/server/companion/PermissionsUtils.java12
-rw-r--r--services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java14
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java35
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java9
-rw-r--r--services/core/java/com/android/server/GestureLauncherService.java15
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java184
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java8
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java25
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java23
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java4
-rw-r--r--services/core/java/com/android/server/app/GameClassifier.java33
-rw-r--r--services/core/java/com/android/server/app/GameClassifierImpl.java49
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java16
-rw-r--r--services/core/java/com/android/server/app/GameServiceConnection.java102
-rw-r--r--services/core/java/com/android/server/app/GameServiceController.java172
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderConfiguration.java94
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderInstance.java36
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderInstanceFactory.java29
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java92
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java294
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderSelector.java34
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java180
-rw-r--r--services/core/java/com/android/server/app/GameSessionRecord.java88
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java1
-rw-r--r--services/core/java/com/android/server/backup/AppSpecificLocalesBackupHelper.java90
-rw-r--r--services/core/java/com/android/server/backup/SystemBackupAgent.java4
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java79
-rw-r--r--services/core/java/com/android/server/communal/CommunalManagerService.java8
-rw-r--r--services/core/java/com/android/server/display/DensityMap.java137
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java132
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java46
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerShellCommand.java18
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java6
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java5
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java13
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java11
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodBindingController.java5
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java28
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java4
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java4
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java12
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java55
-rw-r--r--services/core/java/com/android/server/pm/AppDataHelper.java7
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java5
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java1303
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java49
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceInjector.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java95
-rw-r--r--services/core/java/com/android/server/pm/PackageRemovedInfo.java54
-rw-r--r--services/core/java/com/android/server/pm/ReconcilePackageUtils.java292
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java6
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageHelper.java1716
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageUtils.java1006
-rw-r--r--services/core/java/com/android/server/pm/VerificationParams.java28
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java3
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java247
-rw-r--r--services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java19
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java14
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java17
-rw-r--r--services/core/java/com/android/server/wm/ActivityInterceptorCallback.java25
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java108
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java8
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java62
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowSettings.java13
-rw-r--r--services/core/java/com/android/server/wm/FadeAnimationController.java25
-rw-r--r--services/core/java/com/android/server/wm/FadeRotationAnimationController.java100
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java8
-rw-r--r--services/core/java/com/android/server/wm/NavBarFadeAnimationController.java8
-rw-r--r--services/core/java/com/android/server/wm/PinnedTaskController.java5
-rw-r--r--services/core/java/com/android/server/wm/SeamlessRotator.java14
-rw-r--r--services/core/java/com/android/server/wm/Task.java63
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java31
-rw-r--r--services/core/java/com/android/server/wm/Transition.java11
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java13
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java19
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp6
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd26
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt17
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java115
-rw-r--r--services/java/com/android/server/SystemServer.java37
-rw-r--r--services/selectiontoolbar/Android.bp22
-rw-r--r--services/selectiontoolbar/OWNERS1
-rw-r--r--services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java48
-rw-r--r--services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java68
-rw-r--r--services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerService.java104
-rw-r--r--services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java128
-rw-r--r--services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java43
-rw-r--r--services/tests/mockingservicestests/assets/GameServiceSelectorTest/game_service_metadata_valid.xml3
-rw-r--r--services/tests/mockingservicestests/res/xml/game_service_metadata_no_session_service.xml2
-rw-r--r--services/tests/mockingservicestests/res/xml/game_service_metadata_valid.xml3
-rw-r--r--services/tests/mockingservicestests/res/xml/game_service_metadata_wrong_first_tag.xml3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/FakeGameClassifier.java44
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/FakeGameServiceProviderInstance.java42
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/FakeServiceConnector.java124
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java228
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTests.java455
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java560
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java403
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/DensityMapTest.java143
-rw-r--r--services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java73
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java44
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ScanTests.java8
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java34
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java187
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java37
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java9
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java73
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java37
-rw-r--r--services/usage/java/com/android/server/usage/StorageStatsService.java22
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java9
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java3
-rw-r--r--telephony/common/com/google/android/mms/util/SqliteWrapper.java49
-rw-r--r--telephony/java/android/service/carrier/CarrierService.java98
-rw-r--r--telephony/java/android/service/carrier/ICarrierService.aidl2
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java93
-rw-r--r--telephony/java/android/telephony/ImsManager.java3
-rw-r--r--telephony/java/android/telephony/SignalStrengthUpdateRequest.java8
-rw-r--r--telephony/java/android/telephony/SmsManager.java3
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java53
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java103
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java539
-rw-r--r--telephony/java/android/telephony/TelephonyScanManager.java3
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java50
-rw-r--r--telephony/java/android/telephony/data/DataProfile.java122
-rw-r--r--telephony/java/android/telephony/data/DataService.java31
-rw-r--r--telephony/java/android/telephony/data/DataServiceCallback.java27
-rw-r--r--telephony/java/android/telephony/data/IDataServiceCallback.aidl2
-rw-r--r--telephony/java/android/telephony/euicc/EuiccCardManager.java100
-rw-r--r--telephony/java/android/telephony/euicc/EuiccManager.java2
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java3
-rw-r--r--telephony/java/android/telephony/ims/ImsRcsManager.java3
-rw-r--r--telephony/java/android/telephony/ims/ImsService.java133
-rw-r--r--telephony/java/android/telephony/ims/ProvisioningManager.java2
-rw-r--r--telephony/java/android/telephony/ims/RegistrationManager.java3
-rw-r--r--telephony/java/android/telephony/ims/SipDelegateManager.java2
-rw-r--r--telephony/java/android/telephony/ims/feature/MmTelFeature.java271
-rw-r--r--telephony/java/android/telephony/ims/feature/RcsFeature.java21
-rw-r--r--telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java4
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java146
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java377
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java38
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java37
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java114
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsUtImplBase.java109
-rw-r--r--telephony/java/android/telephony/ims/stub/SipTransportImplBase.java25
-rwxr-xr-xtelephony/java/com/android/internal/telephony/ISub.aidl11
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl10
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl2
699 files changed, 25516 insertions, 9460 deletions
diff --git a/Android.bp b/Android.bp
index 3b8ef617630b..acd3b34d4e7b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,6 +25,18 @@
//
// READ ME: ########################################################
+// TODO(b/21090328): Remove filter after we are ready to.
+soong_config_module_type {
+ name: "java_library_with_nonpublic_deps",
+ module_type: "java_library",
+ config_namespace: "ANDROID",
+ bool_variables: ["include_nonpublic_framework_api"],
+ properties: [
+ "static_libs",
+ "libs",
+ ],
+}
+
package {
default_applicable_licenses: ["frameworks_base_license"],
}
@@ -142,7 +154,7 @@ filegroup {
],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "framework-updatable-stubs-module_libs_api",
static_libs: [
"android.net.ipsec.ike.stubs.module_lib",
@@ -161,11 +173,18 @@ java_library {
"framework-uwb.stubs.module_lib",
"framework-wifi.stubs.module_lib",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs.module_lib",
+ ],
+ },
+ },
sdk_version: "module_current",
visibility: ["//visibility:private"],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "framework-all",
installable: false,
static_libs: [
@@ -186,6 +205,13 @@ java_library {
"framework-wifi.impl",
"updatable-media",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs.module_lib",
+ ],
+ },
+ },
apex_available: ["//apex_available:platform"],
sdk_version: "core_platform",
visibility: [
@@ -362,6 +388,7 @@ java_defaults {
"tv_tuner_resource_manager_aidl_interface-java",
"soundtrigger_middleware-aidl-java",
"modules-utils-preconditions",
+ "modules-utils-synchronous-result-receiver",
"modules-utils-os",
"framework-permission-aidl-java",
"spatializer-aidl-java",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 3b11036495c8..9543fbd38a73 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -23,6 +23,14 @@
// and comparing them against the checked in API signature, and also checking compatibility
// with the latest frozen API signature.
+// TODO(b/21090328): Remove filter after we are ready to.
+soong_config_module_type_import {
+ from: "frameworks/base/Android.bp",
+ module_types: [
+ "java_library_with_nonpublic_deps",
+ ],
+}
+
/////////////////////////////////////////////////////////////////////
// Common metalava configs
/////////////////////////////////////////////////////////////////////
@@ -299,21 +307,35 @@ java_defaults {
visibility: ["//visibility:private"],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android-non-updatable.stubs",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":api-stubs-docs-non-updatable"],
libs: modules_public_stubs,
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
dist: {
dir: "apistubs/android/public",
},
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android-non-updatable.stubs.system",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":system-api-stubs-docs-non-updatable"],
libs: modules_system_stubs,
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
dist: {
dir: "apistubs/android/system",
},
@@ -334,11 +356,18 @@ java_library {
},
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android-non-updatable.stubs.test",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":test-api-stubs-docs-non-updatable"],
libs: modules_system_stubs,
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
dist: {
dir: "apistubs/android/test",
},
@@ -357,21 +386,35 @@ java_defaults {
defaults_visibility: ["//frameworks/base/services"],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android_stubs_current",
static_libs: modules_public_stubs + [
"android-non-updatable.stubs",
"private-stub-annotations-jar",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
defaults: ["android.jar_defaults"],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android_system_stubs_current",
static_libs: modules_system_stubs + [
"android-non-updatable.stubs.system",
"private-stub-annotations-jar",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
defaults: [
"android.jar_defaults",
"android_stubs_dists_default",
@@ -392,7 +435,7 @@ java_library {
],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android_test_stubs_current",
// Modules do not have test APIs, but we want to include their SystemApis, like we include
// the SystemApi of framework-non-updatable-sources.
@@ -400,6 +443,13 @@ java_library {
"android-non-updatable.stubs.test",
"private-stub-annotations-jar",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
defaults: [
"android.jar_defaults",
"android_stubs_dists_default",
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 349802c453b9..b9673f25d680 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1543,11 +1543,25 @@ public class JobInfo implements Parcelable {
*
* <p>Note that trigger URIs can not be used in combination with
* {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}. To continually monitor
- * for content changes, you need to schedule a new JobInfo observing the same URIs
- * before you finish execution of the JobService handling the most recent changes.
+ * for content changes, you need to schedule a new JobInfo using the same job ID and
+ * observing the same URIs in place of calling
+ * {@link JobService#jobFinished(JobParameters, boolean)}. Remember that
+ * {@link JobScheduler#schedule(JobInfo)} stops a running job if it uses the same job ID,
+ * so only call it after you've finished processing the most recent changes (in other words,
+ * call {@link JobScheduler#schedule(JobInfo)} where you would have normally called
+ * {@link JobService#jobFinished(JobParameters, boolean)}.
* Following this pattern will ensure you do not lose any content changes: while your
* job is running, the system will continue monitoring for content changes, and propagate
- * any it sees over to the next job you schedule.</p>
+ * any changes it sees over to the next job you schedule, so you do not have to worry
+ * about missing new changes. <b>Scheduling the new job
+ * before or during processing will cause the current job to be stopped (as described in
+ * {@link JobScheduler#schedule(JobInfo)}), meaning the wakelock will be released for the
+ * current job and your app process may be killed since it will no longer be in a valid
+ * component lifecycle.</b>
+ * Since {@link JobScheduler#schedule(JobInfo)} stops the current job, you do not
+ * need to call {@link JobService#jobFinished(JobParameters, boolean)} if you call
+ * {@link JobScheduler#schedule(JobInfo)} using the same job ID as the
+ * currently running job.</p>
*
* <p>Because setting this property is not compatible with periodic or
* persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp
index 271fc5312f8f..cf384acccb12 100644
--- a/apex/media/service/Android.bp
+++ b/apex/media/service/Android.bp
@@ -40,7 +40,10 @@ java_sdk_library {
],
libs: [
"updatable-media",
+ "modules-annotation-minsdk",
+ "modules-utils-build",
],
+ jarjar_rules: "jarjar_rules.txt",
sdk_version: "system_server_current",
min_sdk_version: "29", // TODO: We may need to bump this at some point.
apex_available: [
diff --git a/apex/media/service/jarjar_rules.txt b/apex/media/service/jarjar_rules.txt
new file mode 100644
index 000000000000..7e37c2be5b4e
--- /dev/null
+++ b/apex/media/service/jarjar_rules.txt
@@ -0,0 +1 @@
+rule com.android.modules.** android.media.internal.@1
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
index 74abf31cc84c..e48f234c5569 100644
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -33,6 +33,7 @@ import android.media.Session2CommandGroup;
import android.media.Session2Token;
import android.media.session.MediaSessionManager;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -46,6 +47,7 @@ import android.util.SparseIntArray;
import android.view.KeyEvent;
import com.android.internal.annotations.GuardedBy;
+import com.android.modules.annotation.MinSdk;
import com.android.server.SystemService;
import java.lang.ref.WeakReference;
@@ -60,6 +62,7 @@ import java.util.concurrent.Executors;
* and their ongoing media playback state.
* @hide
*/
+@MinSdk(Build.VERSION_CODES.S)
public class MediaCommunicationService extends SystemService {
private static final String TAG = "MediaCommunicationSrv";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
diff --git a/core/api/current.txt b/core/api/current.txt
index 0e1a00fe5aaf..d3d0bdfb7018 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1420,6 +1420,7 @@ package android {
field public static final int supportsMultipleDisplays = 16844182; // 0x1010596
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
field public static final int supportsRtl = 16843695; // 0x10103af
+ field public static final int supportsStylusHandwriting;
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
field public static final int supportsUploading = 16843419; // 0x101029b
field public static final int suppressesSpellChecker = 16844355; // 0x1010643
@@ -9312,10 +9313,10 @@ package android.bluetooth {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void disconnect();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean discoverServices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean executeReliableWrite();
- method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method @Deprecated public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @Deprecated public int getConnectionState(android.bluetooth.BluetoothDevice);
method public android.bluetooth.BluetoothDevice getDevice();
- method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ method @Deprecated public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
@@ -10249,6 +10250,7 @@ package android.content {
method @Nullable public String getPackageName();
method public int getUid();
method public boolean isTrusted(@NonNull android.content.Context);
+ method @NonNull public static android.content.AttributionSource myAttributionSource();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.AttributionSource> CREATOR;
}
@@ -11566,6 +11568,7 @@ package android.content {
field public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
field public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
+ field public static final String EXTRA_PREVIOUS_UID = "android.intent.extra.PREVIOUS_UID";
field public static final String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
field public static final String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
field public static final String EXTRA_QUICK_VIEW_FEATURES = "android.intent.extra.QUICK_VIEW_FEATURES";
@@ -11597,6 +11600,7 @@ package android.content {
field public static final String EXTRA_TIMEZONE = "time-zone";
field public static final String EXTRA_TITLE = "android.intent.extra.TITLE";
field public static final String EXTRA_UID = "android.intent.extra.UID";
+ field public static final String EXTRA_UID_CHANGING = "android.intent.extra.UID_CHANGING";
field public static final String EXTRA_USER = "android.intent.extra.USER";
field public static final String EXTRA_USER_INITIATED = "android.intent.extra.USER_INITIATED";
field public static final int FILL_IN_ACTION = 1; // 0x1
@@ -12996,7 +13000,7 @@ package android.content.pm {
field public static final String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
field public static final String FEATURE_CANT_SAVE_STATE = "android.software.cant_save_state";
field public static final String FEATURE_COMPANION_DEVICE_SETUP = "android.software.companion_device_setup";
- field public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
+ field @Deprecated public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
field public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
field public static final String FEATURE_CONTROLS = "android.software.controls";
field public static final String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
@@ -13067,12 +13071,18 @@ package android.content.pm {
field public static final String FEATURE_SIP = "android.software.sip";
field public static final String FEATURE_SIP_VOIP = "android.software.sip.voip";
field public static final String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore";
+ field public static final String FEATURE_TELECOM = "android.software.telecom";
field public static final String FEATURE_TELEPHONY = "android.hardware.telephony";
+ field public static final String FEATURE_TELEPHONY_CALLING = "android.hardware.telephony.calling";
field public static final String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
+ field public static final String FEATURE_TELEPHONY_DATA = "android.hardware.telephony.data";
field public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
field public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
field public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
field public static final String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms";
+ field public static final String FEATURE_TELEPHONY_MESSAGING = "android.hardware.telephony.messaging";
+ field public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio";
+ field public static final String FEATURE_TELEPHONY_SUBSCRIPTION = "android.hardware.telephony.subscription";
field @Deprecated public static final String FEATURE_TELEVISION = "android.hardware.type.television";
field public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
@@ -16589,6 +16599,26 @@ package android.graphics {
method public boolean setUseCompositingLayer(boolean, @Nullable android.graphics.Paint);
}
+ public class RuntimeShader extends android.graphics.Shader {
+ ctor public RuntimeShader(@NonNull String);
+ ctor public RuntimeShader(@NonNull String, boolean);
+ method public boolean isForceOpaque();
+ method public void setColorUniform(@NonNull String, @ColorInt int);
+ method public void setColorUniform(@NonNull String, @ColorLong long);
+ method public void setColorUniform(@NonNull String, @NonNull android.graphics.Color);
+ method public void setFloatUniform(@NonNull String, float);
+ method public void setFloatUniform(@NonNull String, float, float);
+ method public void setFloatUniform(@NonNull String, float, float, float);
+ method public void setFloatUniform(@NonNull String, float, float, float, float);
+ method public void setFloatUniform(@NonNull String, @NonNull float[]);
+ method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
+ method public void setIntUniform(@NonNull String, int);
+ method public void setIntUniform(@NonNull String, int, int);
+ method public void setIntUniform(@NonNull String, int, int, int);
+ method public void setIntUniform(@NonNull String, int, int, int, int);
+ method public void setIntUniform(@NonNull String, @NonNull int[]);
+ }
+
public class Shader {
ctor @Deprecated public Shader();
method public boolean getLocalMatrix(@NonNull android.graphics.Matrix);
@@ -21902,6 +21932,7 @@ package android.media {
public class MediaActionSound {
ctor public MediaActionSound();
method public void load(int);
+ method public static boolean mustPlayShutterSound();
method public void play(int);
method public void release();
field public static final int FOCUS_COMPLETE = 1; // 0x1
@@ -26126,11 +26157,14 @@ package android.media.tv {
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+ field public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
field public static final String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final String COLUMN_MULTI_SERIES_ID = "multi_series_id";
field public static final String COLUMN_POSTER_ART_URI = "poster_art_uri";
field public static final String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
field public static final String COLUMN_REVIEW_RATING = "review_rating";
field public static final String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+ field public static final String COLUMN_SCRAMBLED = "scrambled";
field public static final String COLUMN_SEARCHABLE = "searchable";
field public static final String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
field @Deprecated public static final String COLUMN_SEASON_NUMBER = "season_number";
@@ -26190,7 +26224,9 @@ package android.media.tv {
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+ field public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
field public static final String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final String COLUMN_MULTI_SERIES_ID = "multi_series_id";
field public static final String COLUMN_POSTER_ART_URI = "poster_art_uri";
field public static final String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
field public static final String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
@@ -37894,6 +37930,28 @@ package android.security {
method @Deprecated @NonNull public android.security.KeyPairGeneratorSpec.Builder setSubject(@NonNull javax.security.auth.x500.X500Principal);
}
+ public class KeyStoreException extends java.lang.Exception {
+ method public int getNumericErrorCode();
+ method public boolean isSystemError();
+ method public boolean isTransientFailure();
+ method public boolean requiresUserAuthentication();
+ field public static final int ERROR_ATTESTATION_CHALLENGE_TOO_LARGE = 9; // 0x9
+ field public static final int ERROR_ID_ATTESTATION_FAILURE = 8; // 0x8
+ field public static final int ERROR_INCORRECT_USAGE = 13; // 0xd
+ field public static final int ERROR_INTERNAL_SYSTEM_ERROR = 4; // 0x4
+ field public static final int ERROR_KEYMINT_FAILURE = 10; // 0xa
+ field public static final int ERROR_KEYSTORE_FAILURE = 11; // 0xb
+ field public static final int ERROR_KEYSTORE_UNINITIALIZED = 3; // 0x3
+ field public static final int ERROR_KEY_CORRUPTED = 7; // 0x7
+ field public static final int ERROR_KEY_DOES_NOT_EXIST = 6; // 0x6
+ field public static final int ERROR_KEY_NOT_TEMPORALLY_VALID = 14; // 0xe
+ field public static final int ERROR_KEY_OPERATION_EXPIRED = 15; // 0xf
+ field public static final int ERROR_OTHER = 1; // 0x1
+ field public static final int ERROR_PERMISSION_DENIED = 5; // 0x5
+ field public static final int ERROR_UNIMPLEMENTED = 12; // 0xc
+ field public static final int ERROR_USER_AUTHENTICATION_REQUIRED = 2; // 0x2
+ }
+
@Deprecated public final class KeyStoreParameter implements java.security.KeyStore.ProtectionParameter {
method @Deprecated public boolean isEncryptionRequired();
}
@@ -38728,9 +38786,11 @@ package android.service.carrier {
public abstract class CarrierService extends android.app.Service {
ctor public CarrierService();
- method public final void notifyCarrierNetworkChange(boolean);
+ method @Deprecated public final void notifyCarrierNetworkChange(boolean);
+ method public final void notifyCarrierNetworkChange(int, boolean);
method @CallSuper public android.os.IBinder onBind(android.content.Intent);
- method public abstract android.os.PersistableBundle onLoadConfig(android.service.carrier.CarrierIdentifier);
+ method @Deprecated public abstract android.os.PersistableBundle onLoadConfig(android.service.carrier.CarrierIdentifier);
+ method @Nullable public android.os.PersistableBundle onLoadConfig(int, @Nullable android.service.carrier.CarrierIdentifier);
field public static final String CARRIER_SERVICE_INTERFACE = "android.service.carrier.CarrierService";
}
@@ -41425,6 +41485,7 @@ package android.telephony {
field public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
field public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
field public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
+ field public static final String KEY_CELLULAR_USAGE_SETTING_INT = "cellular_usage_setting_int";
field public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool";
field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string";
@@ -43041,6 +43102,7 @@ package android.telephony {
method public int getSimSlotIndex();
method public int getSubscriptionId();
method public int getSubscriptionType();
+ method public int getUsageSetting();
method public boolean isEmbedded();
method public boolean isOpportunistic();
method public void writeToParcel(android.os.Parcel, int);
@@ -43115,6 +43177,10 @@ package android.telephony {
field public static final int PHONE_NUMBER_SOURCE_UICC = 1; // 0x1
field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
+ field public static final int USAGE_SETTING_DATA_CENTRIC = 2; // 0x2
+ field public static final int USAGE_SETTING_DEFAULT = 0; // 0x0
+ field public static final int USAGE_SETTING_UNKNOWN = -1; // 0xffffffff
+ field public static final int USAGE_SETTING_VOICE_CENTRIC = 1; // 0x1
}
public static class SubscriptionManager.OnOpportunisticSubscriptionsChangedListener {
@@ -43442,8 +43508,10 @@ package android.telephony {
field public static final int DATA_ENABLED_REASON_POLICY = 1; // 0x1
field public static final int DATA_ENABLED_REASON_THERMAL = 3; // 0x3
field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
+ field public static final int DATA_HANDOVER_IN_PROGRESS = 5; // 0x5
field public static final int DATA_SUSPENDED = 3; // 0x3
field public static final int DATA_UNKNOWN = -1; // 0xffffffff
+ field public static final int DEFAULT_PORT_INDEX = 0; // 0x0
field public static final int ERI_FLASH = 2; // 0x2
field public static final int ERI_OFF = 1; // 0x1
field public static final int ERI_ON = 0; // 0x0
@@ -43690,6 +43758,8 @@ package android.telephony.data {
method public String getMmsProxyAddressAsString();
method public int getMmsProxyPort();
method public android.net.Uri getMmsc();
+ method public int getMtuV4();
+ method public int getMtuV6();
method public int getMvnoType();
method public int getNetworkTypeBitmask();
method public String getOperatorNumeric();
@@ -43746,10 +43816,14 @@ package android.telephony.data {
method @NonNull public android.telephony.data.ApnSetting.Builder setMmsProxyAddress(@Nullable String);
method @NonNull public android.telephony.data.ApnSetting.Builder setMmsProxyPort(int);
method @NonNull public android.telephony.data.ApnSetting.Builder setMmsc(@Nullable android.net.Uri);
+ method @NonNull public android.telephony.data.ApnSetting.Builder setMtuV4(int);
+ method @NonNull public android.telephony.data.ApnSetting.Builder setMtuV6(int);
method @NonNull public android.telephony.data.ApnSetting.Builder setMvnoType(int);
method @NonNull public android.telephony.data.ApnSetting.Builder setNetworkTypeBitmask(int);
method @NonNull public android.telephony.data.ApnSetting.Builder setOperatorNumeric(@Nullable String);
method @NonNull public android.telephony.data.ApnSetting.Builder setPassword(@Nullable String);
+ method @NonNull public android.telephony.data.ApnSetting.Builder setPersistent(boolean);
+ method @NonNull public android.telephony.data.ApnSetting.Builder setProfileId(int);
method @NonNull public android.telephony.data.ApnSetting.Builder setProtocol(int);
method @Deprecated public android.telephony.data.ApnSetting.Builder setProxyAddress(java.net.InetAddress);
method @NonNull public android.telephony.data.ApnSetting.Builder setProxyAddress(@Nullable String);
@@ -52807,6 +52881,7 @@ package android.view.inputmethod {
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public CharSequence loadLabel(android.content.pm.PackageManager);
method public boolean shouldShowInInputMethodPicker();
+ method public boolean supportsStylusHandwriting();
method public boolean suppressesSpellChecker();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InputMethodInfo> CREATOR;
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 77569063d87a..850b2e6433a2 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -138,6 +138,8 @@ package android.media {
public class AudioManager {
method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
+ method @NonNull public java.util.List<android.bluetooth.BluetoothCodecConfig> getHwOffloadFormatsSupportedForA2dp();
+ method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio();
method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BtProfileConnectionInfo);
method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setA2dpSuspended(boolean);
method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean);
@@ -227,6 +229,10 @@ package android.net {
field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkSpecifier> CREATOR;
}
+ public final class IpSecManager {
+ field public static final int DIRECTION_FWD = 2; // 0x2
+ }
+
public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
method public int getResourceId();
}
@@ -349,6 +355,7 @@ package android.os {
package android.os.storage {
public class StorageManager {
+ method public long computeStorageCacheBytes(@NonNull java.io.File);
method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int);
method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int);
field public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 1; // 0x1
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 4700ce52a129..2ce4f233e913 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -240,6 +240,7 @@ package android {
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final String READ_PROJECTION_STATE = "android.permission.READ_PROJECTION_STATE";
field public static final String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES";
+ field public static final String READ_SAFETY_CENTER_STATUS = "android.permission.READ_SAFETY_CENTER_STATUS";
field public static final String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
field public static final String READ_SYSTEM_UPDATE_INFO = "android.permission.READ_SYSTEM_UPDATE_INFO";
field public static final String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
@@ -311,6 +312,7 @@ package android {
field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
field public static final String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS";
field public static final String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS";
+ field public static final String UPDATE_DEVICE_MANAGEMENT_RESOURCES = "android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES";
field public static final String UPDATE_DOMAIN_VERIFICATION_USER_SELECTION = "android.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION";
field public static final String UPDATE_FONTS = "android.permission.UPDATE_FONTS";
field public static final String UPDATE_LOCK = "android.permission.UPDATE_LOCK";
@@ -343,6 +345,7 @@ package android {
public static final class R.attr {
field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
+ field public static final int gameSessionService;
field public static final int hotwordDetectionService = 16844326; // 0x1010626
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int minExtensionVersion = 16844305; // 0x1010611
@@ -973,7 +976,8 @@ package android.app.admin {
}
public class DevicePolicyManager {
- method public int checkProvisioningPreCondition(@NonNull String, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int checkProvisioningPreCondition(@NonNull String, @NonNull String);
+ method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
@@ -995,6 +999,7 @@ package android.app.admin {
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
@@ -1062,6 +1067,65 @@ package android.app.admin {
field public static final int STATE_USER_UNMANAGED = 0; // 0x0
}
+ public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
+ method public boolean canDeviceOwnerGrantSensorsPermissions();
+ method public int describeContents();
+ method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
+ method public long getLocalTime();
+ method @Nullable public java.util.Locale getLocale();
+ method @NonNull public String getOwnerName();
+ method @Nullable public String getTimeZone();
+ method public boolean isLeaveAllSystemAppsEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FullyManagedDeviceProvisioningParams> CREATOR;
+ }
+
+ public static final class FullyManagedDeviceProvisioningParams.Builder {
+ ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setCanDeviceOwnerGrantSensorsPermissions(boolean);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setTimeZone(@Nullable String);
+ }
+
+ public final class ManagedProfileProvisioningParams implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.accounts.Account getAccountToMigrate();
+ method @NonNull public String getOwnerName();
+ method @NonNull public android.content.ComponentName getProfileAdminComponentName();
+ method @Nullable public String getProfileName();
+ method public boolean isKeepingAccountOnMigration();
+ method public boolean isLeaveAllSystemAppsEnabled();
+ method public boolean isOrganizationOwnedProvisioning();
+ method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.ManagedProfileProvisioningParams> CREATOR;
+ }
+
+ public static final class ManagedProfileProvisioningParams.Builder {
+ ctor public ManagedProfileProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams build();
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAccountToMigrate(@Nullable android.accounts.Account);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setKeepingAccountOnMigration(boolean);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setOrganizationOwnedProvisioning(boolean);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setProfileName(@Nullable String);
+ }
+
+ public class ProvisioningException extends android.util.AndroidException {
+ ctor public ProvisioningException(@NonNull Exception, int);
+ method public int getProvisioningError();
+ field public static final int ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED = 3; // 0x3
+ field public static final int ERROR_PRE_CONDITION_FAILED = 1; // 0x1
+ field public static final int ERROR_PROFILE_CREATION_FAILED = 2; // 0x2
+ field public static final int ERROR_REMOVE_NON_REQUIRED_APPS_FAILED = 6; // 0x6
+ field public static final int ERROR_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
+ field public static final int ERROR_SET_DEVICE_OWNER_FAILED = 7; // 0x7
+ field public static final int ERROR_STARTING_PROFILE_FAILED = 5; // 0x5
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ }
+
public final class SystemUpdatePolicy implements android.os.Parcelable {
method public android.app.admin.SystemUpdatePolicy.InstallationOption getInstallationOptionAt(long);
field public static final int TYPE_PAUSE = 4; // 0x4
@@ -7565,7 +7629,7 @@ package android.media.tv.tuner.frontend {
method public int getSignalStrength();
method public int getSnr();
method public int getSpectralInversion();
- method @NonNull public int[] getStreamIdList();
+ method @NonNull public int[] getStreamIds();
method public int getSymbolRate();
method @IntRange(from=0, to=65535) public int getSystemId();
method public int getTransmissionMode();
@@ -7612,7 +7676,7 @@ package android.media.tv.tuner.frontend {
field public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH = 6; // 0x6
field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1
field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa
- field public static final int FRONTEND_STATUS_TYPE_STREAM_ID_LIST = 39; // 0x27
+ field public static final int FRONTEND_STATUS_TYPE_STREAM_IDS = 39; // 0x27
field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7
field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d
field public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE = 27; // 0x1b
@@ -9241,6 +9305,7 @@ package android.os {
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount(@NonNull String, @NonNull String);
+ field @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public static final String ACTION_CREATE_SUPERVISED_USER = "android.os.action.CREATE_SUPERVISED_USER";
field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
@@ -10673,12 +10738,35 @@ package android.service.euicc {
package android.service.games {
+ public final class CreateGameSessionRequest implements android.os.Parcelable {
+ ctor public CreateGameSessionRequest(int, @NonNull String);
+ method public int describeContents();
+ method @NonNull public String getGamePackageName();
+ method public int getTaskId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.games.CreateGameSessionRequest> CREATOR;
+ }
+
public class GameService extends android.app.Service {
ctor public GameService();
method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent);
method public void onConnected();
method public void onDisconnected();
- field public static final String SERVICE_INTERFACE = "android.service.games.GameService";
+ field public static final String ACTION_GAME_SERVICE = "android.service.games.action.GAME_SERVICE";
+ field public static final String SERVICE_META_DATA = "android.game_service";
+ }
+
+ public abstract class GameSession {
+ ctor public GameSession();
+ method public void onCreate();
+ method public void onDestroy();
+ }
+
+ public abstract class GameSessionService extends android.app.Service {
+ ctor public GameSessionService();
+ method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent);
+ method @NonNull public abstract android.service.games.GameSession onNewSession(@NonNull android.service.games.CreateGameSessionRequest);
+ field public static final String ACTION_GAME_SESSION_SERVICE = "android.service.games.action.GAME_SESSION_SERVICE";
}
}
@@ -12252,6 +12340,14 @@ package android.telephony {
field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
}
+ public final class SignalStrengthUpdateRequest implements android.os.Parcelable {
+ method public boolean isSystemThresholdReportingRequestedWhileIdle();
+ }
+
+ public static final class SignalStrengthUpdateRequest.Builder {
+ method @NonNull @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public android.telephony.SignalStrengthUpdateRequest.Builder setSystemThresholdReportingRequestedWhileIdle(boolean);
+ }
+
public final class SmsCbCmasInfo implements android.os.Parcelable {
ctor public SmsCbCmasInfo(int, int, int, int, int, int);
method public int describeContents();
@@ -12547,6 +12643,7 @@ package android.telephony {
}
public class TelephonyManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addCarrierPrivilegesListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CarrierPrivilegesListener);
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) @WorkerThread public void bootstrapAuthenticationRequest(int, @NonNull android.net.Uri, @NonNull android.telephony.gba.UaSecurityProtocolIdentifier, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.BootstrapAuthenticationCallback);
method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult changeIccLockPin(@NonNull String, @NonNull String);
@@ -12571,6 +12668,8 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getCarrierServicePackageName();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getCarrierServicePackageNameForLogicalSlot(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorDisplayNumber();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
@@ -12613,10 +12712,14 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoiceActivationState();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmi(String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmiForSubscriber(int, String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int);
- method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int);
- method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
- method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void iccCloseLogicalChannelByPort(int, int, int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelByPort(int, int, @Nullable String, int);
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelByPort(int, int, int, int, int, int, int, @Nullable String);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelByPort(int, int, int, int, int, int, int, int, @Nullable String);
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
@@ -12642,6 +12745,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
method @RequiresPermission(android.Manifest.permission.REBOOT) public int prepareForUnattendedReboot();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeCarrierPrivilegesListener(@NonNull android.telephony.TelephonyManager.CarrierPrivilegesListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestModemActivityInfo(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.ModemActivityInfo,android.telephony.TelephonyManager.ModemActivityInfoException>);
@@ -12818,6 +12922,10 @@ package android.telephony {
field public static final int RESULT_SUCCESS = 0; // 0x0
}
+ public static interface TelephonyManager.CarrierPrivilegesListener {
+ method public void onCarrierPrivilegesChanged(@NonNull java.util.List<java.lang.String>, @NonNull int[]);
+ }
+
public static class TelephonyManager.ModemActivityInfoException extends java.lang.Exception {
method public int getErrorCode();
field public static final int ERROR_INVALID_INFO_RECEIVED = 2; // 0x2
@@ -13003,21 +13111,23 @@ package android.telephony.data {
public final class DataProfile implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public String getApn();
- method public int getAuthType();
- method public int getBearerBitmask();
+ method @Deprecated @NonNull public String getApn();
+ method @Nullable public android.telephony.data.ApnSetting getApnSetting();
+ method @Deprecated public int getAuthType();
+ method @Deprecated public int getBearerBitmask();
method @Deprecated public int getMtu();
- method public int getMtuV4();
- method public int getMtuV6();
- method @Nullable public String getPassword();
- method public int getProfileId();
- method public int getProtocolType();
- method public int getRoamingProtocolType();
- method public int getSupportedApnTypesBitmask();
+ method @Deprecated public int getMtuV4();
+ method @Deprecated public int getMtuV6();
+ method @Deprecated @Nullable public String getPassword();
+ method @Deprecated public int getProfileId();
+ method @Deprecated public int getProtocolType();
+ method @Deprecated public int getRoamingProtocolType();
+ method @Deprecated public int getSupportedApnTypesBitmask();
+ method @Nullable public android.telephony.data.TrafficDescriptor getTrafficDescriptor();
method public int getType();
- method @Nullable public String getUserName();
+ method @Deprecated @Nullable public String getUserName();
method public boolean isEnabled();
- method public boolean isPersistent();
+ method @Deprecated public boolean isPersistent();
method public boolean isPreferred();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataProfile> CREATOR;
@@ -13030,21 +13140,23 @@ package android.telephony.data {
ctor public DataProfile.Builder();
method @NonNull public android.telephony.data.DataProfile build();
method @NonNull public android.telephony.data.DataProfile.Builder enable(boolean);
- method @NonNull public android.telephony.data.DataProfile.Builder setApn(@NonNull String);
- method @NonNull public android.telephony.data.DataProfile.Builder setAuthType(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setBearerBitmask(int);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setApn(@NonNull String);
+ method @NonNull public android.telephony.data.DataProfile.Builder setApnSetting(@NonNull android.telephony.data.ApnSetting);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setAuthType(int);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setBearerBitmask(int);
method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setMtu(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setMtuV4(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setMtuV6(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setPassword(@NonNull String);
- method @NonNull public android.telephony.data.DataProfile.Builder setPersistent(boolean);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setMtuV4(int);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setMtuV6(int);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setPassword(@NonNull String);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setPersistent(boolean);
method @NonNull public android.telephony.data.DataProfile.Builder setPreferred(boolean);
- method @NonNull public android.telephony.data.DataProfile.Builder setProfileId(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setProtocolType(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setRoamingProtocolType(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setSupportedApnTypesBitmask(int);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setProfileId(int);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setProtocolType(int);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setRoamingProtocolType(int);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setSupportedApnTypesBitmask(int);
+ method @NonNull public android.telephony.data.DataProfile.Builder setTrafficDescriptor(@NonNull android.telephony.data.TrafficDescriptor);
method @NonNull public android.telephony.data.DataProfile.Builder setType(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setUserName(@NonNull String);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setUserName(@NonNull String);
}
public abstract class DataService extends android.app.Service {
@@ -13065,6 +13177,7 @@ package android.telephony.data {
method public final int getSlotIndex();
method public final void notifyApnUnthrottled(@NonNull String);
method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+ method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
@@ -13075,6 +13188,7 @@ package android.telephony.data {
public class DataServiceCallback {
method public void onApnUnthrottled(@NonNull String);
method public void onDataCallListChanged(@NonNull java.util.List<android.telephony.data.DataCallResponse>);
+ method public void onDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
method public void onDeactivateDataCallComplete(int);
method public void onRequestDataCallListComplete(int, @NonNull java.util.List<android.telephony.data.DataCallResponse>);
method public void onSetDataProfileComplete(int);
@@ -13182,6 +13296,7 @@ package android.telephony.euicc {
method public void removeNotificationFromList(String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
method public void requestAllProfiles(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo[]>);
method public void requestDefaultSmdpAddress(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.String>);
+ method public void requestEnabledProfileForPort(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
method public void requestEuiccChallenge(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
method public void requestEuiccInfo1(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
method public void requestEuiccInfo2(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
@@ -13205,6 +13320,7 @@ package android.telephony.euicc {
field public static final int RESULT_CALLER_NOT_ALLOWED = -3; // 0xfffffffd
field public static final int RESULT_EUICC_NOT_FOUND = -2; // 0xfffffffe
field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_PROFILE_NOT_FOUND = -4; // 0xfffffffc
field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff
}
@@ -13789,6 +13905,7 @@ package android.telephony.ims {
method public void disableIms(int);
method public void enableIms(int);
method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+ method @NonNull public java.util.concurrent.Executor getExecutor();
method public long getImsServiceCapabilities();
method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
@@ -14407,6 +14524,7 @@ package android.telephony.ims.feature {
public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
ctor public MmTelFeature();
+ ctor public MmTelFeature(@NonNull java.util.concurrent.Executor);
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
method public void changeOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int);
@@ -14440,7 +14558,7 @@ package android.telephony.ims.feature {
}
public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
- ctor @Deprecated public RcsFeature();
+ ctor public RcsFeature();
ctor public RcsFeature(@NonNull java.util.concurrent.Executor);
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
@@ -14545,6 +14663,7 @@ package android.telephony.ims.stub {
}
public class ImsConfigImplBase {
+ ctor public ImsConfigImplBase(@NonNull java.util.concurrent.Executor);
ctor public ImsConfigImplBase();
method public int getConfigInt(int);
method public String getConfigString(int);
@@ -14597,6 +14716,7 @@ package android.telephony.ims.stub {
public class ImsRegistrationImplBase {
ctor public ImsRegistrationImplBase();
+ ctor public ImsRegistrationImplBase(@NonNull java.util.concurrent.Executor);
method public final void onDeregistered(android.telephony.ims.ImsReasonInfo);
method public final void onRegistered(int);
method public final void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes);
@@ -14709,6 +14829,7 @@ package android.telephony.ims.stub {
}
public class SipTransportImplBase {
+ ctor public SipTransportImplBase();
ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor);
method public void createSipDelegate(int, @NonNull android.telephony.ims.DelegateRequest, @NonNull android.telephony.ims.DelegateStateCallback, @NonNull android.telephony.ims.DelegateMessageCallback);
method public void destroySipDelegate(@NonNull android.telephony.ims.stub.SipDelegate, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index fd115676d169..89dc678360f1 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -444,7 +444,6 @@ package android.app.admin {
public class DevicePolicyManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void clearOrganizationId();
method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
- method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
@@ -461,7 +460,6 @@ package android.app.admin {
method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
method @NonNull public static String operationSafetyReasonToString(int);
method @NonNull public static String operationToString(int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void resetDefaultCrossProfileIntentFilters(int);
method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
@@ -511,66 +509,6 @@ package android.app.admin {
field public static final int OPERATION_SWITCH_USER = 2; // 0x2
field public static final int OPERATION_UNINSTALL_CA_CERT = 40; // 0x28
field public static final int OPERATION_WIPE_DATA = 8; // 0x8
- field public static final int PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED = 3; // 0x3
- field public static final int PROVISIONING_RESULT_PRE_CONDITION_FAILED = 1; // 0x1
- field public static final int PROVISIONING_RESULT_PROFILE_CREATION_FAILED = 2; // 0x2
- field public static final int PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED = 6; // 0x6
- field public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
- field public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7; // 0x7
- field public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5; // 0x5
- }
-
- public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
- method public boolean canDeviceOwnerGrantSensorsPermissions();
- method public int describeContents();
- method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
- method public long getLocalTime();
- method @Nullable public java.util.Locale getLocale();
- method @NonNull public String getOwnerName();
- method @Nullable public String getTimeZone();
- method public boolean isLeaveAllSystemAppsEnabled();
- method public void logParams(@NonNull String);
- method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FullyManagedDeviceProvisioningParams> CREATOR;
- }
-
- public static final class FullyManagedDeviceProvisioningParams.Builder {
- ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setDeviceOwnerCanGrantSensorsPermissions(boolean);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setTimeZone(@Nullable String);
- }
-
- public final class ManagedProfileProvisioningParams implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public android.accounts.Account getAccountToMigrate();
- method @NonNull public String getOwnerName();
- method @NonNull public android.content.ComponentName getProfileAdminComponentName();
- method @Nullable public String getProfileName();
- method public boolean isKeepAccountMigrated();
- method public boolean isLeaveAllSystemAppsEnabled();
- method public boolean isOrganizationOwnedProvisioning();
- method public void logParams(@NonNull String);
- method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.ManagedProfileProvisioningParams> CREATOR;
- }
-
- public static final class ManagedProfileProvisioningParams.Builder {
- ctor public ManagedProfileProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams build();
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAccountToMigrate(@Nullable android.accounts.Account);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setKeepAccountMigrated(boolean);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setOrganizationOwnedProvisioning(boolean);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setProfileName(@Nullable String);
- }
-
- public class ProvisioningException extends android.util.AndroidException {
- ctor public ProvisioningException(@NonNull Exception, int);
- method public int getProvisioningResult();
}
public static final class SecurityLog.SecurityEvent implements android.os.Parcelable {
@@ -1976,6 +1914,7 @@ package android.os.storage {
}
public class StorageManager {
+ method public long computeStorageCacheBytes(@NonNull java.io.File);
method @NonNull public static java.util.UUID convert(@NonNull String);
method @NonNull public static String convert(@NonNull java.util.UUID);
method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int);
@@ -2214,8 +2153,8 @@ package android.security {
}
public class KeyStoreException extends java.lang.Exception {
- ctor public KeyStoreException(int, String);
method public int getErrorCode();
+ method public static boolean hasFailureInfoForError(int);
}
}
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index db7ab1a6f379..eb4a355c8ae7 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
@@ -498,6 +499,28 @@ public class ActivityClient {
}
}
+ /**
+ * Shows or hides a Camera app compat toggle for stretched issues with the requested state.
+ *
+ * @param token The token for the window that needs a control.
+ * @param showControl Whether the control should be shown or hidden.
+ * @param transformationApplied Whether the treatment is already applied.
+ * @param callback The callback executed when the user clicks on a control.
+ */
+ void requestCompatCameraControl(Resources res, IBinder token, boolean showControl,
+ boolean transformationApplied, ICompatCameraControlCallback callback) {
+ if (!res.getBoolean(com.android.internal.R.bool
+ .config_isCameraCompatControlForStretchedIssuesEnabled)) {
+ return;
+ }
+ try {
+ getActivityClientController().requestCompatCameraControl(
+ token, showControl, transformationApplied, callback);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
public static ActivityClient getInstance() {
return sInstance.get();
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c0a8c1eb601e..a8894dcd0661 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -561,8 +561,8 @@ public final class ActivityThread extends ClientTransactionHandler
private Configuration mPendingOverrideConfig;
// Used for consolidating configs before sending on to Activity.
private Configuration tmpConfig = new Configuration();
- // Callback used for updating activity override config.
- ViewRootImpl.ActivityConfigCallback configCallback;
+ // Callback used for updating activity override config and camera compat control state.
+ ViewRootImpl.ActivityConfigCallback activityConfigCallback;
ActivityClientRecord nextIdle;
// Indicates whether this activity is currently the topmost resumed one in the system.
@@ -660,13 +660,30 @@ public final class ActivityThread extends ClientTransactionHandler
stopped = false;
hideForNow = false;
nextIdle = null;
- configCallback = (Configuration overrideConfig, int newDisplayId) -> {
- if (activity == null) {
- throw new IllegalStateException(
- "Received config update for non-existing activity");
+ activityConfigCallback = new ViewRootImpl.ActivityConfigCallback() {
+ @Override
+ public void onConfigurationChanged(Configuration overrideConfig,
+ int newDisplayId) {
+ if (activity == null) {
+ throw new IllegalStateException(
+ "Received config update for non-existing activity");
+ }
+ activity.mMainThread.handleActivityConfigurationChanged(
+ ActivityClientRecord.this, overrideConfig, newDisplayId);
}
- activity.mMainThread.handleActivityConfigurationChanged(this, overrideConfig,
- newDisplayId);
+
+ @Override
+ public void requestCompatCameraControl(boolean showControl,
+ boolean transformationApplied, ICompatCameraControlCallback callback) {
+ if (activity == null) {
+ throw new IllegalStateException(
+ "Received camera compat control update for non-existing activity");
+ }
+ ActivityClient.getInstance().requestCompatCameraControl(
+ activity.getResources(), token, showControl, transformationApplied,
+ callback);
+ }
+
};
}
@@ -3672,7 +3689,7 @@ public final class ActivityThread extends ClientTransactionHandler
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
- r.referrer, r.voiceInteractor, window, r.configCallback,
+ r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
r.assistToken, r.shareableActivityToken);
if (customIntent != null) {
@@ -4982,7 +4999,8 @@ public final class ActivityThread extends ClientTransactionHandler
Slog.w(TAG, "Activity top position already set to onTop=" + onTop);
return;
}
- throw new IllegalStateException("Activity top position already set to onTop=" + onTop);
+ // TODO(b/209744518): Remove this short-term workaround while fixing the binder failure.
+ Slog.e(TAG, "Activity top position already set to onTop=" + onTop);
}
r.isTopResumedActivity = onTop;
@@ -5512,8 +5530,8 @@ public final class ActivityThread extends ClientTransactionHandler
} else {
final ViewRootImpl viewRoot = v.getViewRootImpl();
if (viewRoot != null) {
- // Clear the callback to avoid the destroyed activity from receiving
- // configuration changes that are no longer effective.
+ // Clear callbacks to avoid the destroyed activity from receiving
+ // configuration or camera compat changes that are no longer effective.
viewRoot.setActivityConfigCallback(null);
}
wm.removeViewImmediate(v);
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index aba6eb9229f2..83c57c573b82 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -17,6 +17,7 @@
package android.app;
import android.app.ActivityManager;
+import android.app.ICompatCameraControlCallback;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
@@ -143,4 +144,15 @@ interface IActivityClientController {
/** Reports that the splash screen view has attached to activity. */
oneway void splashScreenAttached(in IBinder token);
+
+ /**
+ * Shows or hides a Camera app compat toggle for stretched issues with the requested state.
+ *
+ * @param token The token for the window that needs a control.
+ * @param showControl Whether the control should be shown or hidden.
+ * @param transformationApplied Whether the treatment is already applied.
+ * @param callback The callback executed when the user clicks on a control.
+ */
+ oneway void requestCompatCameraControl(in IBinder token, boolean showControl,
+ boolean transformationApplied, in ICompatCameraControlCallback callback);
}
diff --git a/media/java/android/media/tv/BroadcastInfoType.java b/core/java/android/app/ICompatCameraControlCallback.aidl
index e7a0595d1ce9..1a7f21066630 100644
--- a/media/java/android/media/tv/BroadcastInfoType.java
+++ b/core/java/android/app/ICompatCameraControlCallback.aidl
@@ -14,19 +14,17 @@
* limitations under the License.
*/
-package android.media.tv;
+package android.app;
-/** @hide */
-public final class BroadcastInfoType {
- // todo: change const declaration to intdef in TvInputManager
- public static final int TS = 1;
- public static final int TABLE = 2;
- public static final int SECTION = 3;
- public static final int PES = 4;
- public static final int STREAM_EVENT = 5;
- public static final int DSMCC = 6;
- public static final int TV_PROPRIETARY_FUNCTION = 7;
+/**
+ * This callback allows ActivityRecord to ask the calling View to apply the treatment for stretched
+ * issues affecting camera viewfinders when the user clicks on the camera compat control.
+ *
+ * {@hide}
+ */
+oneway interface ICompatCameraControlCallback {
+
+ void applyCameraCompatTreatment();
- private BroadcastInfoType() {
- }
+ void revertCameraCompatTreatment();
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 370031a14f42..eb4585dd7097 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1259,7 +1259,7 @@ public class Instrumentation {
info, title, parent, id,
(Activity.NonConfigurationInstances)lastNonConfigurationInstance,
new Configuration(), null /* referrer */, null /* voiceInteractor */,
- null /* window */, null /* activityConfigCallback */, null /*assistToken*/,
+ null /* window */, null /* activityCallback */, null /*assistToken*/,
null /*shareableActivityToken*/);
return activity;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 5002a59440e9..85ddff9a9311 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -136,6 +136,7 @@ import android.net.EthernetManager;
import android.net.IEthernetManager;
import android.net.IIpSecService;
import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
import android.net.IPacProxyManager;
import android.net.IVpnManager;
import android.net.IpSecManager;
@@ -227,6 +228,8 @@ import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.IContentCaptureManager;
import android.view.displayhash.DisplayHashManager;
import android.view.inputmethod.InputMethodManager;
+import android.view.selectiontoolbar.ISelectionToolbarManager;
+import android.view.selectiontoolbar.SelectionToolbarManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
import android.view.translation.ITranslationManager;
@@ -366,6 +369,15 @@ public final class SystemServiceRegistry {
return new TextClassificationManager(ctx);
}});
+ registerService(Context.SELECTION_TOOLBAR_SERVICE, SelectionToolbarManager.class,
+ new CachedServiceFetcher<SelectionToolbarManager>() {
+ @Override
+ public SelectionToolbarManager createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(Context.SELECTION_TOOLBAR_SERVICE);
+ return new SelectionToolbarManager(ctx.getOuterContext(),
+ ISelectionToolbarManager.Stub.asInterface(b));
+ }});
+
registerService(Context.FONT_SERVICE, FontManager.class,
new CachedServiceFetcher<FontManager>() {
@Override
@@ -1002,7 +1014,11 @@ public final class SystemServiceRegistry {
new CachedServiceFetcher<NetworkStatsManager>() {
@Override
public NetworkStatsManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- return new NetworkStatsManager(ctx.getOuterContext());
+ // TODO: Replace with an initializer in the module, see
+ // {@code ConnectivityFrameworkInitializer}.
+ final INetworkStatsService service = INetworkStatsService.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE));
+ return new NetworkStatsManager(ctx.getOuterContext(), service);
}});
registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class,
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 95b00c17352d..18f9379c4111 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -19,6 +19,7 @@ package android.app;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -39,6 +40,8 @@ import android.view.DisplayCutout;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
@@ -274,6 +277,51 @@ public class TaskInfo {
*/
public boolean isSleeping;
+ /**
+ * Camera compat control isn't shown because it's not requested by heuristics.
+ * @hide
+ */
+ public static final int CAMERA_COMPAT_CONTROL_HIDDEN = 0;
+
+ /**
+ * Camera compat control is shown with the treatment suggested.
+ * @hide
+ */
+ public static final int CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED = 1;
+
+ /**
+ * Camera compat control is shown to allow reverting the applied treatment.
+ * @hide
+ */
+ public static final int CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED = 2;
+
+ /**
+ * Camera compat control is dismissed by user.
+ * @hide
+ */
+ public static final int CAMERA_COMPAT_CONTROL_DISMISSED = 3;
+
+ /**
+ * Enum for the Camera app compat control states.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "CAMERA_COMPAT_CONTROL_" }, value = {
+ CAMERA_COMPAT_CONTROL_HIDDEN,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED,
+ CAMERA_COMPAT_CONTROL_DISMISSED,
+ })
+ public @interface CameraCompatControlState {};
+
+ /**
+ * State of the Camera app compat control which is used to correct stretched viewfinder
+ * in apps that don't handle all possible configurations and changes between them correctly.
+ * @hide
+ */
+ @CameraCompatControlState
+ public int cameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
+
TaskInfo() {
// Do nothing
}
@@ -342,6 +390,17 @@ public class TaskInfo {
launchCookies.add(cookie);
}
+ /** @hide */
+ public boolean hasCameraCompatControl() {
+ return cameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
+ && cameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
+ }
+
+ /** @hide */
+ public boolean hasCompatUI() {
+ return hasCameraCompatControl() || topActivityInSizeCompat;
+ }
+
/**
* @return {@code true} if this task contains the launch cookie.
* @hide
@@ -394,19 +453,20 @@ public class TaskInfo {
* @return {@code true} if parameters that are important for size compat have changed.
* @hide
*/
- public boolean equalsForSizeCompat(@Nullable TaskInfo that) {
+ public boolean equalsForCompatUi(@Nullable TaskInfo that) {
if (that == null) {
return false;
}
return displayId == that.displayId
&& taskId == that.taskId
&& topActivityInSizeCompat == that.topActivityInSizeCompat
- // Bounds are important if top activity is in size compat
- && (!topActivityInSizeCompat || configuration.windowConfiguration.getBounds()
+ && cameraCompatControlState == that.cameraCompatControlState
+ // Bounds are important if top activity has compat controls.
+ && (!hasCompatUI() || configuration.windowConfiguration.getBounds()
.equals(that.configuration.windowConfiguration.getBounds()))
- && (!topActivityInSizeCompat || configuration.getLayoutDirection()
+ && (!hasCompatUI() || configuration.getLayoutDirection()
== that.configuration.getLayoutDirection())
- && (!topActivityInSizeCompat || isVisible == that.isVisible);
+ && (!hasCompatUI() || isVisible == that.isVisible);
}
/**
@@ -449,6 +509,7 @@ public class TaskInfo {
topActivityInSizeCompat = source.readBoolean();
mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
displayAreaFeatureId = source.readInt();
+ cameraCompatControlState = source.readInt();
}
/**
@@ -492,6 +553,7 @@ public class TaskInfo {
dest.writeBoolean(topActivityInSizeCompat);
dest.writeTypedObject(mTopActivityLocusId, flags);
dest.writeInt(displayAreaFeatureId);
+ dest.writeInt(cameraCompatControlState);
}
@Override
@@ -525,6 +587,22 @@ public class TaskInfo {
+ " topActivityInSizeCompat=" + topActivityInSizeCompat
+ " locusId=" + mTopActivityLocusId
+ " displayAreaFeatureId=" + displayAreaFeatureId
+ + " cameraCompatControlState="
+ + cameraCompatControlStateToString(cameraCompatControlState)
+ "}";
}
+
+ /** @hide */
+ public static String cameraCompatControlStateToString(
+ @CameraCompatControlState int cameraCompatControlState) {
+ switch (cameraCompatControlState) {
+ case CAMERA_COMPAT_CONTROL_HIDDEN: return "hidden";
+ case CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED: return "treatment-suggested";
+ case CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED: return "treatment-applied";
+ case CAMERA_COMPAT_CONTROL_DISMISSED: return "dismissed";
+ default:
+ throw new AssertionError(
+ "Unexpected camera compat control state: " + cameraCompatControlState);
+ }
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ba2828370534..7969cda7b84d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2354,88 +2354,6 @@ public class DevicePolicyManager {
public @interface ProvisioningPreCondition {}
/**
- * Service-specific error code for {@link #provisionFullyManagedDevice} and
- * {@link #createAndProvisionManagedProfile}:
- * Indicates the call to {@link #checkProvisioningPreCondition} returned an error code.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_PRE_CONDITION_FAILED = 1;
-
- /**
- * Service-specific error code for {@link #createAndProvisionManagedProfile}:
- * Indicates the call to {@link UserManager#createProfileForUserEvenWhenDisallowed}
- * returned {@code null}.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_PROFILE_CREATION_FAILED = 2;
-
- /**
- * Service-specific error code for {@link #createAndProvisionManagedProfile}:
- * Indicates the call to {@link PackageManager#installExistingPackageAsUser} has failed.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED = 3;
-
- /**
- * Service-specific error code for {@link #createAndProvisionManagedProfile}:
- * Indicates the call to {@link #setProfileOwner} returned {@code false}.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4;
-
- /**
- * Service-specific error code for {@link #createAndProvisionManagedProfile}:
- * Indicates that starting the newly created profile has failed.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5;
-
- /**
- * Service-specific error code for {@link #provisionFullyManagedDevice}:
- * Indicates that removing the non required apps have failed.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED = 6;
-
- /**
- * Service-specific error code for {@link #provisionFullyManagedDevice}:
- * Indicates the call to {@link #setDeviceOwner} returned {@code false}.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7;
-
- /**
- * Service-specific error codes for {@link #createAndProvisionManagedProfile} and
- * {@link #provisionFullyManagedDevice} indicating all the errors during provisioning.
- *
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "PROVISIONING_RESULT_" }, value = {
- PROVISIONING_RESULT_PRE_CONDITION_FAILED, PROVISIONING_RESULT_PROFILE_CREATION_FAILED,
- PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED,
- PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED,
- PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
- PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED,
- PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED
- })
- public @interface ProvisioningResult {}
-
- /**
* Disable all configurable SystemUI features during LockTask mode. This includes,
* <ul>
* <li>system info area in the status bar (connectivity icons, clock, etc.)
@@ -5821,13 +5739,23 @@ public class DevicePolicyManager {
"android.app.action.CHECK_POLICY_COMPLIANCE";
/**
- * Broadcast action: notify managed provisioning that new managed user is created.
+ * Broadcast action: notify managed provisioning that PO/DO provisioning has completed.
*
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_MANAGED_USER_CREATED =
- "android.app.action.MANAGED_USER_CREATED";
+ public static final String ACTION_PROVISIONING_COMPLETED =
+ "android.app.action.PROVISIONING_COMPLETED";
+
+ /**
+ * Extra for {@link #ACTION_PROVISIONING_COMPLETED} to indicate the provisioning action that has
+ * been completed, this can either be {@link #ACTION_PROVISION_MANAGED_PROFILE},
+ * {@link #ACTION_PROVISION_MANAGED_DEVICE}, or {@link #ACTION_PROVISION_MANAGED_USER}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_PROVISIONING_ACTION =
+ "android.app.extra.PROVISIONING_ACTION";
/**
* Broadcast action: notify system that a new (Android) user was added when the device is
@@ -11424,6 +11352,7 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
public @ProvisioningPreCondition int checkProvisioningPreCondition(
@NonNull String action, @NonNull String packageName) {
try {
@@ -14125,7 +14054,8 @@ public class DevicePolicyManager {
* @hide
*/
@Nullable
- @TestApi
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
public UserHandle createAndProvisionManagedProfile(
@NonNull ManagedProfileProvisioningParams provisioningParams)
throws ProvisioningException {
@@ -14158,7 +14088,7 @@ public class DevicePolicyManager {
*
* @hide
*/
- @TestApi
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
public void provisionFullyManagedDevice(
@NonNull FullyManagedDeviceProvisioningParams provisioningParams)
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
index 80655ddb886f..8c232c012f50 100644
--- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
@@ -21,7 +21,7 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,9 +32,10 @@ import java.util.Locale;
/**
* Params required to provision a fully managed device, see
* {@link DevicePolicyManager#provisionFullyManagedDevice}.
+ *
* @hide
*/
-@TestApi
+@SystemApi
public final class FullyManagedDeviceProvisioningParams implements Parcelable {
private static final String LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM =
"LEAVE_ALL_SYSTEM_APPS_ENABLED";
@@ -92,29 +93,50 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
return localeStr == null ? null : Locale.forLanguageTag(localeStr);
}
+ /**
+ * Returns the device owner's {@link ComponentName}.
+ */
@NonNull
public ComponentName getDeviceAdminComponentName() {
return mDeviceAdminComponentName;
}
+ /**
+ * Returns the device owner's name.
+ */
@NonNull
public String getOwnerName() {
return mOwnerName;
}
+ /**
+ * Returns {@code true} if system apps should be left enabled after provisioning.
+ */
public boolean isLeaveAllSystemAppsEnabled() {
return mLeaveAllSystemAppsEnabled;
}
+ /**
+ * If set, it returns the time zone to set for the device after provisioning, otherwise returns
+ * {@code null};
+ */
@Nullable
public String getTimeZone() {
return mTimeZone;
}
+ /**
+ * If set, it returns the local time to set for the device after provisioning, otherwise returns
+ * 0.
+ */
public long getLocalTime() {
return mLocalTime;
}
+ /**
+ * If set, it returns the {@link Locale} to set for the device after provisioning, otherwise
+ * returns {@code null}.
+ */
@Nullable
public @SuppressLint("UseIcu") Locale getLocale() {
return mLocale;
@@ -130,6 +152,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
/**
* Logs the provisioning params using {@link DevicePolicyEventLogger}.
+ *
+ * @hide
*/
public void logParams(@NonNull String callerPackage) {
requireNonNull(callerPackage);
@@ -232,8 +256,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
* See {@link DevicePolicyManager#EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT}.
*/
@NonNull
- @SuppressLint("MissingGetterMatchingBuilder")
- public Builder setDeviceOwnerCanGrantSensorsPermissions(boolean mayGrant) {
+ public Builder setCanDeviceOwnerGrantSensorsPermissions(boolean mayGrant) {
mDeviceOwnerCanGrantSensorsPermissions = mayGrant;
return this;
}
@@ -261,6 +284,9 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
return 0;
}
+ /**
+ * @hide
+ */
@Override
public String toString() {
return "FullyManagedDeviceProvisioningParams{"
diff --git a/core/java/android/app/admin/ManagedProfileProvisioningParams.java b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
index 1a6099a7f570..ccbef7321be6 100644
--- a/core/java/android/app/admin/ManagedProfileProvisioningParams.java
+++ b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
@@ -21,7 +21,7 @@ import static java.util.Objects.requireNonNull;
import android.accounts.Account;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,7 +33,7 @@ import android.stats.devicepolicy.DevicePolicyEnums;
*
* @hide
*/
-@TestApi
+@SystemApi
public final class ManagedProfileProvisioningParams implements Parcelable {
private static final String LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM =
"LEAVE_ALL_SYSTEM_APPS_ENABLED";
@@ -48,7 +48,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
@Nullable private final Account mAccountToMigrate;
private final boolean mLeaveAllSystemAppsEnabled;
private final boolean mOrganizationOwnedProvisioning;
- private final boolean mKeepAccountMigrated;
+ private final boolean mKeepAccountOnMigration;
private ManagedProfileProvisioningParams(
@@ -58,50 +58,75 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
@Nullable Account accountToMigrate,
boolean leaveAllSystemAppsEnabled,
boolean organizationOwnedProvisioning,
- boolean keepAccountMigrated) {
+ boolean keepAccountOnMigration) {
this.mProfileAdminComponentName = requireNonNull(profileAdminComponentName);
this.mOwnerName = requireNonNull(ownerName);
this.mProfileName = profileName;
this.mAccountToMigrate = accountToMigrate;
this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
this.mOrganizationOwnedProvisioning = organizationOwnedProvisioning;
- this.mKeepAccountMigrated = keepAccountMigrated;
+ this.mKeepAccountOnMigration = keepAccountOnMigration;
}
+ /**
+ * Returns the profile owner's {@link ComponentName}.
+ */
@NonNull
public ComponentName getProfileAdminComponentName() {
return mProfileAdminComponentName;
}
+ /**
+ * Returns the profile owner's name.
+ */
@NonNull
public String getOwnerName() {
return mOwnerName;
}
+ /**
+ * Returns the profile's name if set, otherwise returns {@code null}.
+ */
@Nullable
public String getProfileName() {
return mProfileName;
}
+ /**
+ * If set, it returns the {@link Account} to migrate from the parent profile to the managed
+ * profile after provisioning, otherwise returns {@code null}.
+ */
@Nullable
public Account getAccountToMigrate() {
return mAccountToMigrate;
}
+ /**
+ * Returns {@code true} if system apps should be left enabled after provisioning.
+ */
public boolean isLeaveAllSystemAppsEnabled() {
return mLeaveAllSystemAppsEnabled;
}
+ /**
+ * Returns {@code true} if this is an org owned device.
+ */
public boolean isOrganizationOwnedProvisioning() {
return mOrganizationOwnedProvisioning;
}
- public boolean isKeepAccountMigrated() {
- return mKeepAccountMigrated;
+ /**
+ * Returns {@code true} if the migrated account from {@link #getAccountToMigrate()} should be
+ * kept in parent profile.
+ */
+ public boolean isKeepingAccountOnMigration() {
+ return mKeepAccountOnMigration;
}
/**
* Logs the provisioning params using {@link DevicePolicyEventLogger}.
+ *
+ * @hide
*/
public void logParams(@NonNull String callerPackage) {
requireNonNull(callerPackage);
@@ -109,7 +134,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
logParam(callerPackage, LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM, mLeaveAllSystemAppsEnabled);
logParam(callerPackage, ORGANIZATION_OWNED_PROVISIONING_PARAM,
mOrganizationOwnedProvisioning);
- logParam(callerPackage, KEEP_MIGRATED_ACCOUNT_PARAM, mKeepAccountMigrated);
+ logParam(callerPackage, KEEP_MIGRATED_ACCOUNT_PARAM, mKeepAccountOnMigration);
logParam(callerPackage, ACCOUNT_TO_MIGRATE_PROVIDED_PARAM,
/* value= */ mAccountToMigrate != null);
}
@@ -134,7 +159,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
@Nullable private Account mAccountToMigrate;
private boolean mLeaveAllSystemAppsEnabled;
private boolean mOrganizationOwnedProvisioning;
- private boolean mKeepAccountMigrated;
+ private boolean mKeepingAccountOnMigration;
/**
* Initialize a new {@link Builder) to construct a {@link ManagedProfileProvisioningParams}.
@@ -204,8 +229,8 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
* Defaults to {@code false}.
*/
@NonNull
- public Builder setKeepAccountMigrated(boolean keepAccountMigrated) {
- this.mKeepAccountMigrated = keepAccountMigrated;
+ public Builder setKeepingAccountOnMigration(boolean keepingAccountOnMigration) {
+ this.mKeepingAccountOnMigration = keepingAccountOnMigration;
return this;
}
@@ -223,7 +248,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
mAccountToMigrate,
mLeaveAllSystemAppsEnabled,
mOrganizationOwnedProvisioning,
- mKeepAccountMigrated);
+ mKeepingAccountOnMigration);
}
}
@@ -232,6 +257,9 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
return 0;
}
+ /**
+ * @hide
+ */
@Override
public String toString() {
return "ManagedProfileProvisioningParams{"
@@ -241,7 +269,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
+ ", mAccountToMigrate=" + (mAccountToMigrate == null ? "null" : mAccountToMigrate)
+ ", mLeaveAllSystemAppsEnabled=" + mLeaveAllSystemAppsEnabled
+ ", mOrganizationOwnedProvisioning=" + mOrganizationOwnedProvisioning
- + ", mKeepAccountMigrated=" + mKeepAccountMigrated
+ + ", mKeepAccountOnMigration=" + mKeepAccountOnMigration
+ '}';
}
@@ -253,7 +281,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
dest.writeTypedObject(mAccountToMigrate, flags);
dest.writeBoolean(mLeaveAllSystemAppsEnabled);
dest.writeBoolean(mOrganizationOwnedProvisioning);
- dest.writeBoolean(mKeepAccountMigrated);
+ dest.writeBoolean(mKeepAccountOnMigration);
}
public static final @NonNull Creator<ManagedProfileProvisioningParams> CREATOR =
diff --git a/core/java/android/app/admin/ProvisioningException.java b/core/java/android/app/admin/ProvisioningException.java
index 639859bc3dea..d374c162535a 100644
--- a/core/java/android/app/admin/ProvisioningException.java
+++ b/core/java/android/app/admin/ProvisioningException.java
@@ -15,10 +15,15 @@
*/
package android.app.admin;
+import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
+import android.content.pm.PackageManager;
import android.util.AndroidException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Thrown to indicate a failure during {@link DevicePolicyManager#provisionFullyManagedDevice} and
* {@link DevicePolicyManager#createAndProvisionManagedProfile}.
@@ -26,17 +31,88 @@ import android.util.AndroidException;
* @hide
*
*/
-@TestApi
+@SystemApi
public class ProvisioningException extends AndroidException {
- private final @DevicePolicyManager.ProvisioningResult int mProvisioningResult;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice} and
+ * {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates a generic failure.
+ */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice} and
+ * {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates the call to {@link DevicePolicyManager#checkProvisioningPreCondition} returned an
+ * error code.
+ */
+ public static final int ERROR_PRE_CONDITION_FAILED = 1;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates that the profile creation failed.
+ */
+ public static final int ERROR_PROFILE_CREATION_FAILED = 2;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates the call to {@link PackageManager#installExistingPackageAsUser} has failed.
+ */
+ public static final int ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED = 3;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates that setting the profile owner failed.
+ */
+ public static final int ERROR_SETTING_PROFILE_OWNER_FAILED = 4;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates that starting the newly created profile has failed.
+ */
+ public static final int ERROR_STARTING_PROFILE_FAILED = 5;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice}:
+ * Indicates that removing the non required apps have failed.
+ */
+ public static final int ERROR_REMOVE_NON_REQUIRED_APPS_FAILED = 6;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice}:
+ * Indicates that setting the device owner failed.
+ */
+ public static final int ERROR_SET_DEVICE_OWNER_FAILED = 7;
+
+ /**
+ * Service-specific error codes for {@link DevicePolicyManager#createAndProvisionManagedProfile}
+ * and {@link DevicePolicyManager#provisionFullyManagedDevice} indicating all the errors
+ * during provisioning.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ERROR_" }, value = {
+ ERROR_UNKNOWN, ERROR_PRE_CONDITION_FAILED,
+ ERROR_PROFILE_CREATION_FAILED,
+ ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED,
+ ERROR_SETTING_PROFILE_OWNER_FAILED,
+ ERROR_STARTING_PROFILE_FAILED,
+ ERROR_REMOVE_NON_REQUIRED_APPS_FAILED,
+ ERROR_SET_DEVICE_OWNER_FAILED
+ })
+ public @interface ProvisioningError {}
+
+ private final @ProvisioningError int mProvisioningError;
public ProvisioningException(@NonNull Exception cause,
- @DevicePolicyManager.ProvisioningResult int provisioningResult) {
+ @ProvisioningError int provisioningError) {
super(cause);
- mProvisioningResult = provisioningResult;
+ mProvisioningError = provisioningError;
}
- public @DevicePolicyManager.ProvisioningResult int getProvisioningResult() {
- return mProvisioningResult;
+ public @ProvisioningError int getProvisioningError() {
+ return mProvisioningError;
}
}
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index 567eb4a68a1d..9bb048d7a75f 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -25,6 +25,11 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import com.android.internal.backup.IBackupTransport;
+import com.android.internal.backup.ITransportStatusCallback;
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.Arrays;
+import java.util.List;
/**
* Concrete class that provides a stable-API bridge between IBackupTransport
@@ -659,141 +664,185 @@ public class BackupTransport {
class TransportImpl extends IBackupTransport.Stub {
@Override
- public String name() throws RemoteException {
- return BackupTransport.this.name();
+ public void name(AndroidFuture<String> resultFuture) throws RemoteException {
+ String result = BackupTransport.this.name();
+ resultFuture.complete(result);
}
@Override
- public Intent configurationIntent() throws RemoteException {
- return BackupTransport.this.configurationIntent();
+ public void configurationIntent(AndroidFuture<Intent> resultFuture)
+ throws RemoteException {
+ Intent result = BackupTransport.this.configurationIntent();
+ resultFuture.complete(result);
}
@Override
- public String currentDestinationString() throws RemoteException {
- return BackupTransport.this.currentDestinationString();
+ public void currentDestinationString(AndroidFuture<String> resultFuture)
+ throws RemoteException {
+ String result = BackupTransport.this.currentDestinationString();
+ resultFuture.complete(result);
}
@Override
- public Intent dataManagementIntent() {
- return BackupTransport.this.dataManagementIntent();
+ public void dataManagementIntent(AndroidFuture<Intent> resultFuture)
+ throws RemoteException {
+ Intent result = BackupTransport.this.dataManagementIntent();
+ resultFuture.complete(result);
}
@Override
- public CharSequence dataManagementIntentLabel() {
- return BackupTransport.this.dataManagementIntentLabel();
+ public void dataManagementIntentLabel(AndroidFuture<CharSequence> resultFuture)
+ throws RemoteException {
+ CharSequence result = BackupTransport.this.dataManagementIntentLabel();
+ resultFuture.complete(result);
}
@Override
- public String transportDirName() throws RemoteException {
- return BackupTransport.this.transportDirName();
+ public void transportDirName(AndroidFuture<String> resultFuture) throws RemoteException {
+ String result = BackupTransport.this.transportDirName();
+ resultFuture.complete(result);
}
@Override
- public long requestBackupTime() throws RemoteException {
- return BackupTransport.this.requestBackupTime();
+ public void requestBackupTime(AndroidFuture<Long> resultFuture) throws RemoteException {
+ long result = BackupTransport.this.requestBackupTime();
+ resultFuture.complete(result);
}
@Override
- public int initializeDevice() throws RemoteException {
- return BackupTransport.this.initializeDevice();
+ public void initializeDevice(ITransportStatusCallback callback) throws RemoteException {
+ int result = BackupTransport.this.initializeDevice();
+ callback.onOperationCompleteWithStatus(result);
}
@Override
- public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)
- throws RemoteException {
- return BackupTransport.this.performBackup(packageInfo, inFd, flags);
+ public void performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags,
+ ITransportStatusCallback callback) throws RemoteException {
+ int result = BackupTransport.this.performBackup(packageInfo, inFd, flags);
+ callback.onOperationCompleteWithStatus(result);
}
@Override
- public int clearBackupData(PackageInfo packageInfo) throws RemoteException {
- return BackupTransport.this.clearBackupData(packageInfo);
+ public void clearBackupData(PackageInfo packageInfo, ITransportStatusCallback callback)
+ throws RemoteException {
+ int result = BackupTransport.this.clearBackupData(packageInfo);
+ callback.onOperationCompleteWithStatus(result);
}
@Override
- public int finishBackup() throws RemoteException {
- return BackupTransport.this.finishBackup();
+ public void finishBackup(ITransportStatusCallback callback) throws RemoteException {
+ int result = BackupTransport.this.finishBackup();
+ callback.onOperationCompleteWithStatus(result);
}
@Override
- public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
- return BackupTransport.this.getAvailableRestoreSets();
+ public void getAvailableRestoreSets(AndroidFuture<List<RestoreSet>> resultFuture)
+ throws RemoteException {
+ RestoreSet[] result = BackupTransport.this.getAvailableRestoreSets();
+ resultFuture.complete(Arrays.asList(result));
}
@Override
- public long getCurrentRestoreSet() throws RemoteException {
- return BackupTransport.this.getCurrentRestoreSet();
+ public void getCurrentRestoreSet(AndroidFuture<Long> resultFuture)
+ throws RemoteException {
+ long result = BackupTransport.this.getCurrentRestoreSet();
+ resultFuture.complete(result);
}
@Override
- public int startRestore(long token, PackageInfo[] packages) throws RemoteException {
- return BackupTransport.this.startRestore(token, packages);
+ public void startRestore(long token, PackageInfo[] packages,
+ ITransportStatusCallback callback) throws RemoteException {
+ int result = BackupTransport.this.startRestore(token, packages);
+ callback.onOperationCompleteWithStatus(result);
}
@Override
- public RestoreDescription nextRestorePackage() throws RemoteException {
- return BackupTransport.this.nextRestorePackage();
+ public void nextRestorePackage(AndroidFuture<RestoreDescription> resultFuture)
+ throws RemoteException {
+ RestoreDescription result = BackupTransport.this.nextRestorePackage();
+ resultFuture.complete(result);
}
@Override
- public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
- return BackupTransport.this.getRestoreData(outFd);
+ public void getRestoreData(ParcelFileDescriptor outFd,
+ ITransportStatusCallback callback) throws RemoteException {
+ int result = BackupTransport.this.getRestoreData(outFd);
+ callback.onOperationCompleteWithStatus(result);
}
@Override
- public void finishRestore() throws RemoteException {
+ public void finishRestore(ITransportStatusCallback callback)
+ throws RemoteException {
BackupTransport.this.finishRestore();
+ callback.onOperationComplete();
}
@Override
- public long requestFullBackupTime() throws RemoteException {
- return BackupTransport.this.requestFullBackupTime();
+ public void requestFullBackupTime(AndroidFuture<Long> resultFuture)
+ throws RemoteException {
+ long result = BackupTransport.this.requestFullBackupTime();
+ resultFuture.complete(result);
}
@Override
- public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket,
- int flags) throws RemoteException {
- return BackupTransport.this.performFullBackup(targetPackage, socket, flags);
+ public void performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket,
+ int flags, ITransportStatusCallback callback) throws RemoteException {
+ int result = BackupTransport.this.performFullBackup(targetPackage, socket, flags);
+ callback.onOperationCompleteWithStatus(result);
}
@Override
- public int checkFullBackupSize(long size) {
- return BackupTransport.this.checkFullBackupSize(size);
+ public void checkFullBackupSize(long size, ITransportStatusCallback callback)
+ throws RemoteException {
+ int result = BackupTransport.this.checkFullBackupSize(size);
+ callback.onOperationCompleteWithStatus(result);
}
@Override
- public int sendBackupData(int numBytes) throws RemoteException {
- return BackupTransport.this.sendBackupData(numBytes);
+ public void sendBackupData(int numBytes, ITransportStatusCallback callback)
+ throws RemoteException {
+ int result = BackupTransport.this.sendBackupData(numBytes);
+ callback.onOperationCompleteWithStatus(result);
}
@Override
- public void cancelFullBackup() throws RemoteException {
+ public void cancelFullBackup(ITransportStatusCallback callback) throws RemoteException {
BackupTransport.this.cancelFullBackup();
+ callback.onOperationComplete();
}
@Override
- public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup)
- throws RemoteException {
- return BackupTransport.this.isAppEligibleForBackup(targetPackage, isFullBackup);
+ public void isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup,
+ AndroidFuture<Boolean> resultFuture) throws RemoteException {
+ boolean result = BackupTransport.this.isAppEligibleForBackup(targetPackage,
+ isFullBackup);
+ resultFuture.complete(result);
}
@Override
- public long getBackupQuota(String packageName, boolean isFullBackup) {
- return BackupTransport.this.getBackupQuota(packageName, isFullBackup);
+ public void getBackupQuota(String packageName, boolean isFullBackup,
+ AndroidFuture<Long> resultFuture) throws RemoteException {
+ long result = BackupTransport.this.getBackupQuota(packageName, isFullBackup);
+ resultFuture.complete(result);
}
@Override
- public int getTransportFlags() {
- return BackupTransport.this.getTransportFlags();
+ public void getTransportFlags(AndroidFuture<Integer> resultFuture) throws RemoteException {
+ int result = BackupTransport.this.getTransportFlags();
+ resultFuture.complete(result);
}
@Override
- public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
- return BackupTransport.this.getNextFullRestoreDataChunk(socket);
+ public void getNextFullRestoreDataChunk(ParcelFileDescriptor socket,
+ ITransportStatusCallback callback) throws RemoteException {
+ int result = BackupTransport.this.getNextFullRestoreDataChunk(socket);
+ callback.onOperationCompleteWithStatus(result);
}
@Override
- public int abortFullRestore() {
- return BackupTransport.this.abortFullRestore();
+ public void abortFullRestore(ITransportStatusCallback callback) throws RemoteException {
+ int result = BackupTransport.this.abortFullRestore();
+ callback.onOperationCompleteWithStatus(result);
}
}
}
diff --git a/core/java/android/app/cloudsearch/OWNERS b/core/java/android/app/cloudsearch/OWNERS
new file mode 100644
index 000000000000..aa4da3b4bee0
--- /dev/null
+++ b/core/java/android/app/cloudsearch/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 758286
+
+huiwu@google.com
+srazdan@google.com
diff --git a/core/java/android/app/compat/ChangeIdStateQuery.java b/core/java/android/app/compat/ChangeIdStateQuery.java
index 91765f78a2b0..7598d6c90d3d 100644
--- a/core/java/android/app/compat/ChangeIdStateQuery.java
+++ b/core/java/android/app/compat/ChangeIdStateQuery.java
@@ -85,6 +85,14 @@ final class ChangeIdStateQuery {
@Override
public int hashCode() {
- return Objects.hash(type, changeId, packageName, uid, userId);
+ int result = 1;
+ result = 31 * result + type;
+ result = 31 * result + (int) (changeId ^ (changeId >>> 32));
+ if (packageName != null) {
+ result = 31 * result + packageName.hashCode();
+ }
+ result = 31 * result + uid;
+ result = 31 * result + userId;
+ return result;
}
}
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index fd1b9e3bede2..db3a1921c1ba 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -105,7 +105,7 @@ public final class AppPredictor {
e.rethrowAsRuntimeException();
}
- mCloseGuard.open("close");
+ mCloseGuard.open("AppPredictor.close");
}
/**
diff --git a/core/java/android/app/search/SearchSession.java b/core/java/android/app/search/SearchSession.java
index a5425a20655a..2cd1d96190b0 100644
--- a/core/java/android/app/search/SearchSession.java
+++ b/core/java/android/app/search/SearchSession.java
@@ -106,7 +106,7 @@ public final class SearchSession implements AutoCloseable{
e.rethrowFromSystemServer();
}
- mCloseGuard.open("close");
+ mCloseGuard.open("SearchSession.close");
}
/**
diff --git a/core/java/android/app/smartspace/SmartspaceSession.java b/core/java/android/app/smartspace/SmartspaceSession.java
index 9199581c3149..b523be2cc7e9 100644
--- a/core/java/android/app/smartspace/SmartspaceSession.java
+++ b/core/java/android/app/smartspace/SmartspaceSession.java
@@ -107,7 +107,7 @@ public final class SmartspaceSession implements AutoCloseable {
e.rethrowFromSystemServer();
}
- mCloseGuard.open("close");
+ mCloseGuard.open("SmartspaceSession.close");
}
/**
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index d66dc637743d..8b9cec17a196 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,17 +32,19 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
@@ -271,7 +275,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
IBluetoothA2dp.class.getName()) {
@Override
public IBluetoothA2dp getServiceInterface(IBinder service) {
- return IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothA2dp.Stub.asInterface(service);
}
};
@@ -322,17 +326,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
@UnsupportedAppUsage
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.connect(device);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connectWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -364,17 +372,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
@UnsupportedAppUsage
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.disconnect(device);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnectWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -385,19 +397,24 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevicesWithAttribution(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevicesWithAttribution(mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -408,20 +425,25 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStatesWithAttribution(states,
+ mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStatesWithAttribution(states,
- mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -432,18 +454,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @BtProfileState int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionState(device);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionStateWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
}
+ return defaultValue;
}
/**
@@ -471,18 +496,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
@UnsupportedAppUsage(trackingBug = 171933273)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && ((device == null) || isValidDevice(device))) {
- return service.setActiveDevice(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && ((device == null) || isValidDevice(device))) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setActiveDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -499,18 +527,24 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getActiveDevice() {
if (VDBG) log("getActiveDevice()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ final BluetoothDevice defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<BluetoothDevice> recv =
+ new SynchronousResultReceiver();
+ service.getActiveDevice(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getActiveDevice(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return null;
}
+ return defaultValue;
}
/**
@@ -555,22 +589,23 @@ public final class BluetoothA2dp implements BluetoothProfile {
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -589,19 +624,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return BluetoothAdapter.connectionPolicyToPriority(
- service.getPriority(device, mAttributionSource));
- }
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.PRIORITY_OFF;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.PRIORITY_OFF;
- }
+ return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
}
/**
@@ -623,18 +646,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
})
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionPolicy(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
+ return defaultValue;
}
/**
@@ -646,17 +672,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresNoPermission
public boolean isAvrcpAbsoluteVolumeSupported() {
if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.isAvrcpAbsoluteVolumeSupported();
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isAvrcpAbsoluteVolumeSupported(recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
- return false;
}
+ return defaultValue;
}
/**
@@ -669,14 +699,16 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setAvrcpAbsoluteVolume(int volume) {
if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
service.setAvrcpAbsoluteVolume(volume, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
}
}
@@ -689,18 +721,22 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isA2dpPlaying(BluetoothDevice device) {
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.isA2dpPlaying(device, mAttributionSource);
+ if (DBG) log("isA2dpPlaying(" + device + ")");
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isA2dpPlaying(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -729,8 +765,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
/**
* Gets the current codec status (configuration and capability).
*
- * @param device the remote Bluetooth device. If null, use the current
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @return the current codec status
* @hide
*/
@@ -742,26 +777,28 @@ public final class BluetoothA2dp implements BluetoothProfile {
public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
verifyDeviceNotNull(device, "getCodecStatus");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.getCodecStatus(device, mAttributionSource);
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ final IBluetoothA2dp service = getService();
+ final BluetoothCodecStatus defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<BluetoothCodecStatus> recv =
+ new SynchronousResultReceiver();
+ service.getCodecStatus(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
- return null;
}
+ return defaultValue;
}
/**
* Sets the codec configuration preference.
*
- * @param device the remote Bluetooth device. If null, use the current
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @param codecConfig the codec configuration preference
* @hide
*/
@@ -777,24 +814,23 @@ public final class BluetoothA2dp implements BluetoothProfile {
Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
throw new IllegalArgumentException("codecConfig cannot be null");
}
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
service.setCodecConfigPreference(device, codecConfig, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
- return;
}
}
/**
* Enables the optional codecs.
*
- * @param device the remote Bluetooth device. If null, use the currect
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -810,8 +846,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
/**
* Disables the optional codecs.
*
- * @param device the remote Bluetooth device. If null, use the currect
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -827,26 +862,25 @@ public final class BluetoothA2dp implements BluetoothProfile {
/**
* Enables or disables the optional codecs.
*
- * @param device the remote Bluetooth device. If null, use the currect
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @param enable if true, enable the optional codecs, other disable them
*/
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
if (enable) {
service.enableOptionalCodecs(device, mAttributionSource);
} else {
service.disableOptionalCodecs(device, mAttributionSource);
}
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
- return;
}
}
@@ -864,18 +898,23 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@OptionalCodecsSupportStatus
public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) {
+ if (DBG) log("isOptionalCodecsSupported(" + device + ")");
verifyDeviceNotNull(device, "isOptionalCodecsSupported");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.supportsOptionalCodecs(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = OPTIONAL_CODECS_SUPPORT_UNKNOWN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.supportsOptionalCodecs(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in supportsOptionalCodecs()", e);
- return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
}
+ return defaultValue;
}
/**
@@ -892,18 +931,23 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@OptionalCodecsPreferenceStatus
public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) {
+ if (DBG) log("isOptionalCodecsEnabled(" + device + ")");
verifyDeviceNotNull(device, "isOptionalCodecsEnabled");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.getOptionalCodecsEnabled(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = OPTIONAL_CODECS_PREF_UNKNOWN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getOptionalCodecsEnabled(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return OPTIONAL_CODECS_PREF_UNKNOWN;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in getOptionalCodecsEnabled()", e);
- return OPTIONAL_CODECS_PREF_UNKNOWN;
}
+ return defaultValue;
}
/**
@@ -921,24 +965,24 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device,
@OptionalCodecsPreferenceStatus int value) {
+ if (DBG) log("setOptionalCodecsEnabled(" + device + ")");
verifyDeviceNotNull(device, "setOptionalCodecsEnabled");
- try {
- if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
- && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
- && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
- Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
- return;
- }
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
+ if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
+ && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
+ && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
+ Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
+ return;
+ }
+ final IBluetoothA2dp service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
service.setOptionalCodecsEnabled(device, value, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return;
}
}
@@ -961,17 +1005,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
})
public @Type int getDynamicBufferSupport() {
if (VDBG) log("getDynamicBufferSupport()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.getDynamicBufferSupport(mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = DYNAMIC_BUFFER_SUPPORT_NONE;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getDynamicBufferSupport(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return DYNAMIC_BUFFER_SUPPORT_NONE;
- } catch (RemoteException e) {
- Log.e(TAG, "failed to get getDynamicBufferSupport, error: ", e);
- return DYNAMIC_BUFFER_SUPPORT_NONE;
}
+ return defaultValue;
}
/**
@@ -992,17 +1040,22 @@ public final class BluetoothA2dp implements BluetoothProfile {
})
public @Nullable BufferConstraints getBufferConstraints() {
if (VDBG) log("getBufferConstraints()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.getBufferConstraints(mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final BufferConstraints defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<BufferConstraints> recv =
+ new SynchronousResultReceiver();
+ service.getBufferConstraints(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- return null;
}
+ return defaultValue;
}
/**
@@ -1027,17 +1080,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
Log.e(TAG, "Trying to set audio buffer length to a negative value: " + value);
return false;
}
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.setBufferLengthMillis(codec, value, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setBufferLengthMillis(codec, value, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- return false;
}
+ return defaultValue;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index 924dc55f31a0..59416818ceb3 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -29,14 +31,16 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Bluetooth A2DP Sink
@@ -86,7 +90,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
"BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) {
@Override
public IBluetoothA2dpSink getServiceInterface(IBinder service) {
- return IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothA2dpSink.Stub.asInterface(service);
}
};
@@ -140,16 +144,20 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -181,16 +189,20 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -204,17 +216,23 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -228,18 +246,23 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -251,18 +274,22 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
- if (VDBG) log("getState(" + device + ")");
+ if (VDBG) log("getConnectionState(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -282,16 +309,21 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
if (VDBG) log("getAudioConfig(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final BluetoothAudioConfig defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getAudioConfig(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return null;
+ final SynchronousResultReceiver<BluetoothAudioConfig> recv =
+ new SynchronousResultReceiver();
+ service.getAudioConfig(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
+ return defaultValue;
}
/**
@@ -337,20 +369,22 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -393,16 +427,20 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
/**
@@ -420,17 +458,22 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
})
public boolean isAudioPlaying(@NonNull BluetoothDevice device) {
+ if (VDBG) log("isAudioPlaying(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.isA2dpPlaying(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isA2dpPlaying(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 429751215763..2d1ecfb5e052 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -797,7 +797,7 @@ public final class BluetoothAdapter {
@RequiresNoPermission
public static synchronized BluetoothAdapter getDefaultAdapter() {
if (sAdapter == null) {
- sAdapter = createAdapter(BluetoothManager.resolveAttributionSource(null));
+ sAdapter = createAdapter(AttributionSource.myAttributionSource());
}
return sAdapter;
}
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index 536dfb0a66dd..81fc3e11e9e1 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -23,13 +25,15 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
@@ -93,8 +97,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
"BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) {
@Override
public IBluetoothAvrcpController getServiceInterface(IBinder service) {
- return IBluetoothAvrcpController.Stub.asInterface(
- Binder.allowBlocking(service));
+ return IBluetoothAvrcpController.Stub.asInterface(service);
}
};
@@ -130,19 +133,24 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -153,20 +161,24 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -177,18 +189,21 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothAvrcpController service = getService();
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -201,17 +216,22 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getPlayerSettings");
BluetoothAvrcpPlayerSettings settings = null;
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ final BluetoothAvrcpPlayerSettings defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- settings = service.getPlayerSettings(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
- return null;
+ final SynchronousResultReceiver<BluetoothAvrcpPlayerSettings> recv =
+ new SynchronousResultReceiver();
+ service.getPlayerSettings(device, mAttributionSource, recv);
+ settings = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return settings;
+ return defaultValue;
}
/**
@@ -222,18 +242,21 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.setPlayerApplicationSetting(plAppSetting, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setPlayerApplicationSetting(plAppSetting, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -245,18 +268,20 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
+ keyState);
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource);
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
return;
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
index f0a8df0fa72f..ba57ec472a6e 100644
--- a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
+++ b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
@@ -17,6 +17,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
@@ -27,13 +29,14 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -41,6 +44,7 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Bluetooth CSIP set coordinator.
@@ -229,8 +233,7 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
IBluetoothCsipSetCoordinator.class.getName()) {
@Override
public IBluetoothCsipSetCoordinator getServiceInterface(IBinder service) {
- return IBluetoothCsipSetCoordinator.Stub.asInterface(
- Binder.allowBlocking(service));
+ return IBluetoothCsipSetCoordinator.Stub.asInterface(service);
}
};
@@ -283,26 +286,27 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
public
@Nullable UUID groupLock(int groupId, @Nullable @CallbackExecutor Executor executor,
@Nullable ClientLockCallback cb) {
- if (VDBG) {
- log("groupLockSet()");
- }
+ if (VDBG) log("groupLockSet()");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled()) {
- IBluetoothCsipSetCoordinatorLockCallback delegate = null;
- if ((executor != null) && (cb != null)) {
- delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb);
- }
- return service.groupLock(groupId, delegate, mAttributionSource).getUuid();
+ final UUID defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ IBluetoothCsipSetCoordinatorLockCallback delegate = null;
+ if ((executor != null) && (cb != null)) {
+ delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb);
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ try {
+ final SynchronousResultReceiver<ParcelUuid> recv = new SynchronousResultReceiver();
+ service.groupLock(groupId, delegate, mAttributionSource, recv);
+ final ParcelUuid ret = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ return ret == null ? defaultValue : ret.getUuid();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return null;
}
+ return defaultValue;
}
/**
@@ -315,27 +319,26 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean groupUnlock(@NonNull UUID lockUuid) {
- if (VDBG) {
- log("groupLockSet()");
- }
+ if (VDBG) log("groupLockSet()");
if (lockUuid == null) {
return false;
}
-
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled()) {
- service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
return true;
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -348,22 +351,22 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @NonNull Map getGroupUuidMapByDevice(@Nullable BluetoothDevice device) {
- if (VDBG) {
- log("getGroupUuidMapByDevice()");
- }
+ if (VDBG) log("getGroupUuidMapByDevice()");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled()) {
- return service.getGroupUuidMapByDevice(device, mAttributionSource);
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ final Map defaultValue = new HashMap<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Map> recv = new SynchronousResultReceiver();
+ service.getGroupUuidMapByDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return new HashMap<>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new HashMap<>();
}
+ return defaultValue;
}
/**
@@ -376,22 +379,23 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @NonNull List<Integer> getAllGroupIds(@Nullable ParcelUuid uuid) {
- if (VDBG) {
- log("getAllGroupIds()");
- }
+ if (VDBG) log("getAllGroupIds()");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled()) {
- return service.getAllGroupIds(uuid, mAttributionSource);
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ final List<Integer> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<Integer>> recv =
+ new SynchronousResultReceiver();
+ service.getAllGroupIds(uuid, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return new ArrayList<Integer>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<Integer>();
}
+ return defaultValue;
}
/**
@@ -399,22 +403,23 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
*/
@Override
public @NonNull List<BluetoothDevice> getConnectedDevices() {
- if (VDBG) {
- log("getConnectedDevices()");
- }
+ if (VDBG) log("getConnectedDevices()");
final IBluetoothCsipSetCoordinator service = getService();
- if (service != null && isEnabled()) {
- try {
- return service.getConnectedDevices(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
- }
- }
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -422,24 +427,24 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
*/
@Override
public
- @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
- @NonNull int[] states) {
- if (VDBG) {
- log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")");
- }
+ @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[] states) {
+ if (VDBG) log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")");
final IBluetoothCsipSetCoordinator service = getService();
- if (service != null && isEnabled()) {
- try {
- return service.getDevicesMatchingConnectionStates(states, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
- }
- }
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -447,24 +452,23 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
*/
@Override
public
- @BluetoothProfile.BtProfileState int getConnectionState(
- @Nullable BluetoothDevice device) {
- if (VDBG) {
- log("getState(" + device + ")");
- }
+ @BluetoothProfile.BtProfileState int getConnectionState(@Nullable BluetoothDevice device) {
+ if (VDBG) log("getState(" + device + ")");
final IBluetoothCsipSetCoordinator service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
- }
- }
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -484,26 +488,24 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setConnectionPolicy(
@Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) {
- if (DBG) {
- log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- }
+ if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -521,22 +523,22 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
- if (VDBG) {
- log("getConnectionPolicy(" + device + ")");
- }
+ if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.getConnectionPolicy(device, mAttributionSource);
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 9ff4dc3bb125..93f026860856 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1177,7 +1177,7 @@ public final class BluetoothDevice implements Parcelable, Attributable {
mAddress = address;
mAddressType = ADDRESS_TYPE_PUBLIC;
- mAttributionSource = BluetoothManager.resolveAttributionSource(null);
+ mAttributionSource = AttributionSource.myAttributionSource();
}
/** {@hide} */
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 4e7c01ad2db1..fe8d1ba80e33 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -1806,32 +1806,33 @@ public final class BluetoothGatt implements BluetoothProfile {
}
/**
- * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+ * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
* with {@link BluetoothProfile#GATT} as argument
- *
* @throws UnsupportedOperationException
*/
@Override
@RequiresNoPermission
+ @Deprecated
public int getConnectionState(BluetoothDevice device) {
throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
}
/**
- * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+ * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
* with {@link BluetoothProfile#GATT} as argument
*
* @throws UnsupportedOperationException
*/
@Override
@RequiresNoPermission
+ @Deprecated
public List<BluetoothDevice> getConnectedDevices() {
throw new UnsupportedOperationException(
"Use BluetoothManager#getConnectedDevices instead.");
}
/**
- * Not supported - please use
+ * @deprecated Not supported - please use
* {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
* with {@link BluetoothProfile#GATT} as first argument
*
@@ -1839,6 +1840,7 @@ public final class BluetoothGatt implements BluetoothProfile {
*/
@Override
@RequiresNoPermission
+ @Deprecated
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
throw new UnsupportedOperationException(
"Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 17c02cdbf8d5..f2a6276ce8fe 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -31,7 +33,6 @@ import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -41,8 +42,11 @@ import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* Public API for controlling the Bluetooth Headset Service. This includes both
@@ -479,16 +483,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connectWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -520,16 +528,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnectWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -541,18 +553,23 @@ public final class BluetoothHeadset implements BluetoothProfile {
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevicesWithAttribution(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevicesWithAttribution(mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -564,18 +581,23 @@ public final class BluetoothHeadset implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -587,16 +609,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionStateWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -622,20 +648,22 @@ public final class BluetoothHeadset implements BluetoothProfile {
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -655,18 +683,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return BluetoothAdapter.connectionPolicyToPriority(
- service.getPriority(device, mAttributionSource));
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.PRIORITY_OFF;
- }
- }
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.PRIORITY_OFF;
+ return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
}
/**
@@ -689,16 +706,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
/**
@@ -713,15 +734,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) {
if (DBG) log("isNoiseReductionSupported()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.isNoiseReductionSupported(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isNoiseReductionSupported(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -736,15 +762,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) {
if (DBG) log("isVoiceRecognitionSupported()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.isVoiceRecognitionSupported(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isVoiceRecognitionSupported(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -775,15 +806,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.startVoiceRecognition(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.startVoiceRecognition(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -804,15 +840,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.stopVoiceRecognition(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.stopVoiceRecognition(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -827,15 +868,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean isAudioConnected(BluetoothDevice device) {
if (VDBG) log("isAudioConnected()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.isAudioConnected(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isAudioConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -861,17 +907,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
final IBluetoothHeadset service = mService;
- if (service != null && !isDisabled()) {
+ final int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (!isDisabled()) {
try {
- return service.getAudioState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getAudioState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -889,15 +938,17 @@ public final class BluetoothHeadset implements BluetoothProfile {
public void setAudioRouteAllowed(boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- service.setAudioRouteAllowed(allowed, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setAudioRouteAllowed(allowed, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
}
@@ -912,17 +963,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean getAudioRouteAllowed() {
if (VDBG) log("getAudioRouteAllowed");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getAudioRouteAllowed(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getAudioRouteAllowed(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -937,15 +991,17 @@ public final class BluetoothHeadset implements BluetoothProfile {
public void setForceScoAudio(boolean forced) {
if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- service.setForceScoAudio(forced, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setForceScoAudio(forced, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
}
@@ -962,16 +1018,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean isAudioOn() {
if (VDBG) log("isAudioOn()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.isAudioOn(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isAudioOn(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
-
+ return defaultValue;
}
/**
@@ -996,18 +1056,22 @@ public final class BluetoothHeadset implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connectAudio() {
+ if (VDBG) log("connectAudio()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.connectAudio(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connectAudio(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1025,18 +1089,22 @@ public final class BluetoothHeadset implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnectAudio() {
+ if (VDBG) log("disconnectAudio()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.disconnectAudio(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnectAudio(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1070,17 +1138,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean startScoUsingVirtualVoiceCall() {
if (DBG) log("startScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.startScoUsingVirtualVoiceCall(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.startScoUsingVirtualVoiceCall(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1105,17 +1176,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean stopScoUsingVirtualVoiceCall() {
if (DBG) log("stopScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.stopScoUsingVirtualVoiceCall(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.stopScoUsingVirtualVoiceCall(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1135,16 +1209,16 @@ public final class BluetoothHeadset implements BluetoothProfile {
public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
int type, String name) {
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
service.phoneStateChanged(numActive, numHeld, callState, number, type, name,
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
}
@@ -1161,16 +1235,18 @@ public final class BluetoothHeadset implements BluetoothProfile {
public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
String number, int type) {
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
service.clccResponse(index, direction, status, mode, mpty, number, type,
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
}
@@ -1202,18 +1278,21 @@ public final class BluetoothHeadset implements BluetoothProfile {
throw new IllegalArgumentException("command is null");
}
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.sendVendorSpecificResultCode(device, command, arg,
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendVendorSpecificResultCode(device, command, arg,
+ mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1247,17 +1326,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
Log.d(TAG, "setActiveDevice: " + device);
}
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && (device == null || isValidDevice(device))) {
- try {
- return service.setActiveDevice(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && (device == null || isValidDevice(device))) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setActiveDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1273,22 +1355,25 @@ public final class BluetoothHeadset implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getActiveDevice() {
- if (VDBG) {
- Log.d(TAG, "getActiveDevice");
- }
+ if (VDBG) Log.d(TAG, "getActiveDevice");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final BluetoothDevice defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<BluetoothDevice> recv =
+ new SynchronousResultReceiver();
+ service.getActiveDevice(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getActiveDevice(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return null;
+ return defaultValue;
}
/**
@@ -1303,21 +1388,22 @@ public final class BluetoothHeadset implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean isInbandRingingEnabled() {
- if (DBG) {
- log("isInbandRingingEnabled()");
- }
+ if (DBG) log("isInbandRingingEnabled()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
- try {
- return service.isInbandRingingEnabled(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isInbandRingingEnabled(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1337,7 +1423,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothHeadset.Stub.asInterface(Binder.allowBlocking(service));
+ mService = IBluetoothHeadset.Stub.asInterface(service);
mHandler.sendMessage(mHandler.obtainMessage(
MESSAGE_HEADSET_SERVICE_CONNECTED));
}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 2ef37101e23d..7d7a7f798bb9 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
@@ -25,15 +27,17 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* Public API to control Hands Free Profile (HFP role only).
@@ -432,7 +436,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
"BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) {
@Override
public IBluetoothHeadsetClient getServiceInterface(IBinder service) {
- return IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothHeadsetClient.Stub.asInterface(service);
}
};
@@ -479,18 +483,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -507,18 +514,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -531,19 +541,24 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -558,20 +573,24 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -585,18 +604,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -634,22 +656,23 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -686,18 +709,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final @ConnectionPolicy int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
/**
@@ -715,17 +741,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.startVoiceRecognition(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.startVoiceRecognition(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -739,20 +769,23 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId,
- String atCommand) {
+ public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) {
if (DBG) log("sendVendorSpecificCommand()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -770,17 +803,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.stopVoiceRecognition(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.stopVoiceRecognition(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -793,18 +830,24 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
if (DBG) log("getCurrentCalls()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final List<BluetoothHeadsetClientCall> defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
+ final SynchronousResultReceiver<List<BluetoothHeadsetClientCall>> recv =
+ new SynchronousResultReceiver();
+ service.getCurrentCalls(device, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getCurrentCalls(device, mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
+ return defaultValue;
}
/**
@@ -817,17 +860,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Bundle getCurrentAgEvents(BluetoothDevice device) {
if (DBG) log("getCurrentAgEvents()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final Bundle defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getCurrentAgEvents(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Bundle> recv = new SynchronousResultReceiver();
+ service.getCurrentAgEvents(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
+ return defaultValue;
}
/**
@@ -844,17 +891,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean acceptCall(BluetoothDevice device, int flag) {
if (DBG) log("acceptCall()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.acceptCall(device, flag, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.acceptCall(device, flag, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -868,17 +919,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean holdCall(BluetoothDevice device) {
if (DBG) log("holdCall()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.holdCall(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.holdCall(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -897,17 +952,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean rejectCall(BluetoothDevice device) {
if (DBG) log("rejectCall()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.rejectCall(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.rejectCall(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -930,17 +989,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
if (DBG) log("terminateCall()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.terminateCall(device, call, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.terminateCall(device, call, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -961,17 +1024,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean enterPrivateMode(BluetoothDevice device, int index) {
if (DBG) log("enterPrivateMode()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.enterPrivateMode(device, index, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.enterPrivateMode(device, index, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -991,17 +1058,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean explicitCallTransfer(BluetoothDevice device) {
if (DBG) log("explicitCallTransfer()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.explicitCallTransfer(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.explicitCallTransfer(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -1017,18 +1088,24 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
if (DBG) log("dial()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final BluetoothHeadsetClientCall defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
+ final SynchronousResultReceiver<BluetoothHeadsetClientCall> recv =
+ new SynchronousResultReceiver();
+ service.dial(device, number, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.dial(device, number, mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
+ return defaultValue;
}
/**
@@ -1045,17 +1122,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendDTMF(BluetoothDevice device, byte code) {
if (DBG) log("sendDTMF()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.sendDTMF(device, code, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendDTMF(device, code, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -1074,17 +1155,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getLastVoiceTagNumber(BluetoothDevice device) {
if (DBG) log("getLastVoiceTagNumber()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getLastVoiceTagNumber(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getLastVoiceTagNumber(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -1097,17 +1182,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ final int defaultValue = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getAudioState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getAudioState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
} else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ return defaultValue;
}
return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
}
@@ -1123,17 +1212,18 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- service.setAudioRouteAllowed(device, allowed, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setAudioRouteAllowed(device, allowed, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
}
@@ -1148,19 +1238,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getAudioRouteAllowed(BluetoothDevice device) {
if (VDBG) log("getAudioRouteAllowed");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getAudioRouteAllowed(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getAudioRouteAllowed(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1175,19 +1267,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connectAudio(BluetoothDevice device) {
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ if (VDBG) log("connectAudio");
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.connectAudio(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connectAudio(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1202,19 +1297,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnectAudio(BluetoothDevice device) {
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ if (VDBG) log("disconnectAudio");
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.disconnectAudio(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnectAudio(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1226,19 +1324,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Bundle getCurrentAgFeatures(BluetoothDevice device) {
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ if (VDBG) log("getCurrentAgFeatures");
+ final IBluetoothHeadsetClient service = getService();
+ final Bundle defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getCurrentAgFeatures(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Bundle> recv = new SynchronousResultReceiver();
+ service.getCurrentAgFeatures(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return null;
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index a00b20d99f96..339a75fe0fbe 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -28,14 +30,16 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Hearing Aid profile.
@@ -136,7 +140,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
"BluetoothHearingAid", IBluetoothHearingAid.class.getName()) {
@Override
public IBluetoothHearingAid getServiceInterface(IBinder service) {
- return IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothHearingAid.Stub.asInterface(service);
}
};
@@ -181,16 +185,20 @@ public final class BluetoothHearingAid implements BluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.connect(device, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -223,16 +231,20 @@ public final class BluetoothHearingAid implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.disconnect(device, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -244,17 +256,23 @@ public final class BluetoothHearingAid implements BluetoothProfile {
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -267,18 +285,23 @@ public final class BluetoothHearingAid implements BluetoothProfile {
@NonNull int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -291,17 +314,20 @@ public final class BluetoothHearingAid implements BluetoothProfile {
@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionState(device, mAttributionSource);
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
}
+ return defaultValue;
}
/**
@@ -330,18 +356,20 @@ public final class BluetoothHearingAid implements BluetoothProfile {
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && ((device == null) || isValidDevice(device))) {
- service.setActiveDevice(device, mAttributionSource);
- return true;
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && ((device == null) || isValidDevice(device))) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setActiveDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -359,17 +387,23 @@ public final class BluetoothHearingAid implements BluetoothProfile {
public @NonNull List<BluetoothDevice> getActiveDevices() {
if (VDBG) log("getActiveDevices()");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getActiveDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getActiveDevices(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<>();
}
+ return defaultValue;
}
/**
@@ -416,21 +450,22 @@ public final class BluetoothHearingAid implements BluetoothProfile {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
verifyDeviceNotNull(device, "setConnectionPolicy");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -474,17 +509,20 @@ public final class BluetoothHearingAid implements BluetoothProfile {
if (VDBG) log("getConnectionPolicy(" + device + ")");
verifyDeviceNotNull(device, "getConnectionPolicy");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionPolicy(device, mAttributionSource);
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
+ return defaultValue;
}
/**
@@ -519,19 +557,18 @@ public final class BluetoothHearingAid implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setVolume(int volume) {
if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
-
final IBluetoothHearingAid service = getService();
- try {
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- return;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setVolume(volume, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
-
- if (!isEnabled()) return;
-
- service.setVolume(volume, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
}
}
@@ -552,24 +589,23 @@ public final class BluetoothHearingAid implements BluetoothProfile {
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
})
public long getHiSyncId(@NonNull BluetoothDevice device) {
- if (VDBG) {
- log("getHiSyncId(" + device + ")");
- }
+ if (VDBG) log("getHiSyncId(" + device + ")");
verifyDeviceNotNull(device, "getConnectionPolicy");
final IBluetoothHearingAid service = getService();
- try {
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- return HI_SYNC_ID_INVALID;
+ final long defaultValue = HI_SYNC_ID_INVALID;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Long> recv = new SynchronousResultReceiver();
+ service.getHiSyncId(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
-
- if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
-
- return service.getHiSyncId(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return HI_SYNC_ID_INVALID;
}
+ return defaultValue;
}
/**
@@ -583,21 +619,22 @@ public final class BluetoothHearingAid implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getDeviceSide(BluetoothDevice device) {
- if (VDBG) {
- log("getDeviceSide(" + device + ")");
- }
+ if (VDBG) log("getDeviceSide(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getDeviceSide(device, mAttributionSource);
+ final int defaultValue = SIDE_LEFT;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getDeviceSide(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return SIDE_LEFT;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return SIDE_LEFT;
}
+ return defaultValue;
}
/**
@@ -611,21 +648,22 @@ public final class BluetoothHearingAid implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getDeviceMode(BluetoothDevice device) {
- if (VDBG) {
- log("getDeviceMode(" + device + ")");
- }
+ if (VDBG) log("getDeviceMode(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getDeviceMode(device, mAttributionSource);
+ final int defaultValue = MODE_MONAURAL;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getDeviceMode(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return MODE_MONAURAL;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return MODE_MONAURAL;
}
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index f5b444fd17cb..44a355b5f75c 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -26,14 +28,16 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
/**
* Provides the public APIs to control the Bluetooth HID Device profile.
@@ -431,7 +435,7 @@ public final class BluetoothHidDevice implements BluetoothProfile {
"BluetoothHidDevice", IBluetoothHidDevice.class.getName()) {
@Override
public IBluetoothHidDevice getServiceInterface(IBinder service) {
- return IBluetoothHidDevice.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothHidDevice.Stub.asInterface(service);
}
};
@@ -455,18 +459,23 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return new ArrayList<>();
+ return defaultValue;
}
/** {@inheritDoc} */
@@ -475,19 +484,23 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return new ArrayList<>();
+ return defaultValue;
}
/** {@inheritDoc} */
@@ -496,17 +509,20 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final int defaultValue = STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -555,18 +571,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
}
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = result;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
CallbackWrapper cbw = new CallbackWrapper(executor, callback, mAttributionSource);
- result = service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource, recv);
+ result = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -582,20 +601,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean unregisterApp() {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- result = service.unregisterApp(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.unregisterApp(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -609,20 +629,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- result = service.sendReport(device, id, data, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendReport(device, id, data, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -637,20 +658,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- result = service.replyReport(device, type, id, data, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.replyReport(device, type, id, data, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -663,20 +685,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean reportError(BluetoothDevice device, byte error) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- result = service.reportError(device, error, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.reportError(device, error, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -689,18 +712,20 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public String getUserAppName() {
final IBluetoothHidDevice service = getService();
-
- if (service != null) {
+ final String defaultValue = "";
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getUserAppName(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<String> recv = new SynchronousResultReceiver();
+ service.getUserAppName(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return "";
+ return defaultValue;
}
/**
@@ -714,20 +739,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(BluetoothDevice device) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- result = service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -740,20 +766,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- result = service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -781,23 +808,24 @@ public final class BluetoothHidDevice implements BluetoothProfile {
})
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
- log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- try {
- final IBluetoothHidDevice service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ final IBluetoothHidDevice service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 121aa1611522..ecbeddf2b853 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -28,13 +30,15 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
@@ -244,7 +248,7 @@ public final class BluetoothHidHost implements BluetoothProfile {
"BluetoothHidHost", IBluetoothHidHost.class.getName()) {
@Override
public IBluetoothHidHost getServiceInterface(IBinder service) {
- return IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothHidHost.Stub.asInterface(service);
}
};
@@ -292,16 +296,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -334,16 +342,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -358,17 +370,23 @@ public final class BluetoothHidHost implements BluetoothProfile {
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -382,18 +400,23 @@ public final class BluetoothHidHost implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -411,16 +434,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
throw new IllegalArgumentException("device must not be null");
}
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -469,20 +496,22 @@ public final class BluetoothHidHost implements BluetoothProfile {
throw new IllegalArgumentException("device must not be null");
}
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -528,16 +557,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
throw new IllegalArgumentException("device must not be null");
}
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
private boolean isEnabled() {
@@ -561,18 +594,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean virtualUnplug(BluetoothDevice device) {
if (DBG) log("virtualUnplug(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.virtualUnplug(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.virtualUnplug(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
-
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
-
+ return defaultValue;
}
/**
@@ -588,16 +623,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean getProtocolMode(BluetoothDevice device) {
if (VDBG) log("getProtocolMode(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getProtocolMode(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getProtocolMode(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -613,16 +652,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
if (DBG) log("setProtocolMode(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.setProtocolMode(device, protocolMode, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setProtocolMode(device, protocolMode, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -645,17 +688,21 @@ public final class BluetoothHidHost implements BluetoothProfile {
+ "bufferSize=" + bufferSize);
}
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getReport(device, reportType, reportId, bufferSize,
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getReport(device, reportType, reportId, bufferSize, mAttributionSource,
+ recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -673,16 +720,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean setReport(BluetoothDevice device, byte reportType, String report) {
if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.setReport(device, reportType, report, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setReport(device, reportType, report, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -699,16 +750,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean sendData(BluetoothDevice device, String report) {
if (DBG) log("sendData(" + device + "), report=" + report);
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.sendData(device, report, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendData(device, report, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -724,16 +779,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean getIdleTime(BluetoothDevice device) {
if (DBG) log("getIdletime(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getIdleTime(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getIdleTime(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -750,16 +809,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.setIdleTime(device, idleTime, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setIdleTime(device, idleTime, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java
index 34398eba5d28..15db686b3be4 100644
--- a/core/java/android/bluetooth/BluetoothLeAudio.java
+++ b/core/java/android/bluetooth/BluetoothLeAudio.java
@@ -17,6 +17,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,14 +29,16 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the LeAudio profile.
@@ -331,7 +335,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
IBluetoothLeAudio.class.getName()) {
@Override
public IBluetoothLeAudio getServiceInterface(IBinder service) {
- return IBluetoothLeAudio.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothLeAudio.Stub.asInterface(service);
}
};
@@ -385,17 +389,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(@Nullable BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled() && isValidDevice(device)) {
- return service.connect(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -425,17 +433,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(@Nullable BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled() && isValidDevice(device)) {
- return service.disconnect(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -446,18 +458,24 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
+ final IBluetoothLeAudio service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -469,19 +487,24 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
@NonNull int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
+ final IBluetoothLeAudio service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -493,18 +516,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionState(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
}
+ return defaultValue;
}
/**
@@ -531,19 +557,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()
- && ((device == null) || isValidDevice(device))) {
- service.setActiveDevice(device, mAttributionSource);
- return true;
+ final IBluetoothLeAudio service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && ((device == null) || isValidDevice(device))) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setActiveDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -557,19 +585,25 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getActiveDevices() {
- if (VDBG) log("getActiveDevices()");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
+ if (VDBG) log("getActiveDevice()");
+ final IBluetoothLeAudio service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getActiveDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getActiveDevices(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<>();
}
+ return defaultValue;
}
/**
@@ -583,17 +617,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getGroupId(@NonNull BluetoothDevice device) {
if (VDBG) log("getGroupId()");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
- return service.getGroupId(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final int defaultValue = GROUP_ID_INVALID;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getGroupId(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return GROUP_ID_INVALID;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return GROUP_ID_INVALID;
}
+ return defaultValue;
}
/**
@@ -606,17 +644,18 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED})
public void setVolume(int volume) {
if (VDBG) log("setVolume(vol: " + volume + " )");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
- service.setVolume(volume, mAttributionSource);
- return;
+ final IBluetoothLeAudio service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setVolume(volume, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return;
}
}
@@ -635,16 +674,20 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
public boolean groupAddNode(int group_id, @NonNull BluetoothDevice device) {
if (VDBG) log("groupAddNode()");
final IBluetoothLeAudio service = getService();
- try {
- if (service != null && mAdapter.isEnabled()) {
- return service.groupAddNode(group_id, device, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.groupAddNode(group_id, device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -663,16 +706,20 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
public boolean groupRemoveNode(int group_id, @NonNull BluetoothDevice device) {
if (VDBG) log("groupRemoveNode()");
final IBluetoothLeAudio service = getService();
- try {
- if (service != null && mAdapter.isEnabled()) {
- return service.groupRemoveNode(group_id, device, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.groupRemoveNode(group_id, device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -695,22 +742,23 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -728,18 +776,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionPolicy(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
+ return defaultValue;
}
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index c93de41b5b36..fef6f225dd3b 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -16,14 +16,10 @@
package android.bluetooth;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
-import android.app.ActivityThread;
-import android.app.AppGlobals;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.AttributionSource;
@@ -68,37 +64,11 @@ public final class BluetoothManager {
* @hide
*/
public BluetoothManager(Context context) {
- mAttributionSource = resolveAttributionSource(context);
+ mAttributionSource = (context != null) ? context.getAttributionSource() :
+ AttributionSource.myAttributionSource();
mAdapter = BluetoothAdapter.createAdapter(mAttributionSource);
}
- /** {@hide} */
- public static @NonNull AttributionSource resolveAttributionSource(@Nullable Context context) {
- AttributionSource res = null;
- if (context != null) {
- res = context.getAttributionSource();
- }
- if (res == null) {
- res = ActivityThread.currentAttributionSource();
- }
- if (res == null) {
- int uid = android.os.Process.myUid();
- if (uid == android.os.Process.ROOT_UID) {
- uid = android.os.Process.SYSTEM_UID;
- }
- try {
- res = new AttributionSource.Builder(uid)
- .setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0])
- .build();
- } catch (RemoteException ignored) {
- }
- }
- if (res == null) {
- throw new IllegalStateException("Failed to resolve AttributionSource");
- }
- return res;
- }
-
/**
* Get the BLUETOOTH Adapter for this device.
*
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 474e41f4aa19..56e497262421 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresNoPermission;
@@ -28,15 +30,17 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth MAP
@@ -87,7 +91,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
"BluetoothMap", IBluetoothMap.class.getName()) {
@Override
public IBluetoothMap getServiceInterface(IBinder service) {
- return IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothMap.Stub.asInterface(service);
}
};
@@ -142,17 +146,20 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public int getState() {
if (VDBG) log("getState()");
final IBluetoothMap service = getService();
- if (service != null) {
- try {
- return service.getState(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final int defaultValue = BluetoothMap.STATE_ERROR;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getState(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothMap.STATE_ERROR;
+ return defaultValue;
}
/**
@@ -168,18 +175,23 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
final IBluetoothMap service = getService();
- if (service != null) {
+ final BluetoothDevice defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<BluetoothDevice> recv =
+ new SynchronousResultReceiver();
+ service.getClient(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getClient(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) log(Log.getStackTraceString(new Throwable()));
}
- return null;
+ return defaultValue;
}
/**
@@ -194,17 +206,20 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
final IBluetoothMap service = getService();
- if (service != null) {
- try {
- return service.isConnected(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -233,16 +248,20 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothMap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -284,17 +303,23 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothMap service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -309,18 +334,23 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothMap service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -335,16 +365,21 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothMap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv =
+ new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -390,20 +425,22 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothMap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -446,16 +483,20 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothMap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 8a3f80164aeb..03536f9aad39 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -29,15 +31,17 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
import android.net.Uri;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth MAP MCE Profile.
@@ -180,7 +184,7 @@ public final class BluetoothMapClient implements BluetoothProfile {
"BluetoothMapClient", IBluetoothMapClient.class.getName()) {
@Override
public IBluetoothMapClient getServiceInterface(IBinder service) {
- return IBluetoothMapClient.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothMapClient.Stub.asInterface(service);
}
};
@@ -221,17 +225,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean isConnected(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null) {
- try {
- return service.isConnected(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -248,17 +255,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
final IBluetoothMapClient service = getService();
- if (service != null) {
- try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -277,15 +287,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "disconnect(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -300,17 +315,23 @@ public final class BluetoothMapClient implements BluetoothProfile {
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) Log.d(TAG, "getConnectedDevices()");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<>();
+ return defaultValue;
}
/**
@@ -325,18 +346,23 @@ public final class BluetoothMapClient implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<>();
+ return defaultValue;
}
/**
@@ -351,16 +377,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
public int getConnectionState(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver<>();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -405,20 +435,22 @@ public final class BluetoothMapClient implements BluetoothProfile {
@ConnectionPolicy int connectionPolicy) {
if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -460,16 +492,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
/**
@@ -494,18 +530,8 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection<Uri> contacts,
@NonNull String message, @Nullable PendingIntent sentIntent,
@Nullable PendingIntent deliveredIntent) {
- if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
- final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.sendMessage(device, contacts.toArray(new Uri[contacts.size()]),
- message, sentIntent, deliveredIntent, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
- }
- }
- return false;
+ return sendMessage(device, contacts.toArray(new Uri[contacts.size()]), message, sentIntent,
+ deliveredIntent);
}
/**
@@ -531,16 +557,21 @@ public final class BluetoothMapClient implements BluetoothProfile {
PendingIntent sentIntent, PendingIntent deliveredIntent) {
if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent,
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendMessage(device, contacts, message, sentIntent, deliveredIntent,
+ mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return false;
+ return defaultValue;
}
/**
@@ -558,15 +589,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean getUnreadMessages(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getUnreadMessages(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getUnreadMessages(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return false;
+ return defaultValue;
}
/**
@@ -580,13 +616,21 @@ public final class BluetoothMapClient implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isUploadingSupported(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "isUploadingSupported(" + device + ")");
final IBluetoothMapClient service = getService();
- try {
- return (service != null && isEnabled() && isValidDevice(device))
- && ((service.getSupportedFeatures(device, mAttributionSource)
- & UPLOADING_FEATURE_BITMASK) > 0);
- } catch (RemoteException e) {
- Log.e(TAG, e.getMessage());
+ final int defaultValue = 0;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getSupportedFeatures(device, mAttributionSource, recv);
+ return (recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue)
+ & UPLOADING_FEATURE_BITMASK) > 0;
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
return false;
}
@@ -615,16 +659,21 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean setMessageStatus(BluetoothDevice device, String handle, int status) {
if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device) && handle != null &&
- (status == READ || status == UNREAD || status == UNDELETED || status == DELETED)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device) && handle != null && (status == READ
+ || status == UNREAD || status == UNDELETED || status == DELETED)) {
try {
- return service.setMessageStatus(device, handle, status, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setMessageStatus(device, handle, status, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return false;
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index ac7a52d8f636..d4ad4ef47acd 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -28,16 +30,18 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth Pan
@@ -188,7 +192,7 @@ public final class BluetoothPan implements BluetoothProfile {
"BluetoothPan", IBluetoothPan.class.getName()) {
@Override
public IBluetoothPan getServiceInterface(IBinder service) {
- return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothPan.Stub.asInterface(service);
}
};
@@ -249,16 +253,20 @@ public final class BluetoothPan implements BluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothPan service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -289,16 +297,20 @@ public final class BluetoothPan implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothPan service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -322,22 +334,23 @@ public final class BluetoothPan implements BluetoothProfile {
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- try {
- final IBluetoothPan service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final IBluetoothPan service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -354,17 +367,23 @@ public final class BluetoothPan implements BluetoothProfile {
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothPan service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -381,18 +400,23 @@ public final class BluetoothPan implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothPan service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -409,16 +433,20 @@ public final class BluetoothPan implements BluetoothProfile {
public int getConnectionState(@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothPan service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -438,11 +466,16 @@ public final class BluetoothPan implements BluetoothProfile {
String pkgName = mContext.getOpPackageName();
if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
final IBluetoothPan service = getService();
- if (service != null && isEnabled()) {
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- service.setBluetoothTethering(value, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setBluetoothTethering(value, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
}
@@ -459,14 +492,20 @@ public final class BluetoothPan implements BluetoothProfile {
public boolean isTetheringOn() {
if (VDBG) log("isTetheringOn()");
final IBluetoothPan service = getService();
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.isTetheringOn(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isTetheringOn(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return false;
+ return defaultValue;
}
@UnsupportedAppUsage
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index e13792918103..de2db9c2ca86 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -25,15 +25,10 @@ import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -96,10 +91,6 @@ public class BluetoothPbap implements BluetoothProfile {
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
- private volatile IBluetoothPbap mService;
- private final Context mContext;
- private ServiceListener mServiceListener;
- private final BluetoothAdapter mAdapter;
private final AttributionSource mAttributionSource;
/** @hide */
@@ -113,87 +104,25 @@ public class BluetoothPbap implements BluetoothProfile {
*/
public static final int RESULT_CANCELED = 2;
- @SuppressLint("AndroidFrameworkBluetoothPermission")
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- log("onBluetoothStateChange: up=" + up);
- if (!up) {
- doUnbind();
- } else {
- doBind();
- }
+ private BluetoothAdapter mAdapter;
+ private final BluetoothProfileConnector<IBluetoothPbap> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.PBAP, "BluetoothPbap",
+ IBluetoothPbap.class.getName()) {
+ @Override
+ public IBluetoothPbap getServiceInterface(IBinder service) {
+ return IBluetoothPbap.Stub.asInterface(service);
}
- };
+ };
/**
* Create a BluetoothPbap proxy object.
*
* @hide
*/
- public BluetoothPbap(Context context, ServiceListener l, BluetoothAdapter adapter) {
- mContext = context;
- mServiceListener = l;
+ public BluetoothPbap(Context context, ServiceListener listener, BluetoothAdapter adapter) {
mAdapter = adapter;
mAttributionSource = adapter.getAttributionSource();
-
- // Preserve legacy compatibility where apps were depending on
- // registerStateChangeCallback() performing a permissions check which
- // has been relaxed in modern platform versions
- if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
- && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Need BLUETOOTH permission");
- }
-
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "", re);
- }
- }
- doBind();
- }
-
- @SuppressLint("AndroidFrameworkRequiresPermission")
- boolean doBind() {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- log("Binding service...");
- Intent intent = new Intent(IBluetoothPbap.class.getName());
- ComponentName comp = intent.resolveSystemService(
- mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT)) {
- Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
- return false;
- }
- }
- } catch (SecurityException se) {
- Log.e(TAG, "", se);
- return false;
- }
- }
- return true;
- }
-
- private void doUnbind() {
- synchronized (mConnection) {
- if (mService != null) {
- log("Unbinding service...");
- try {
- mContext.unbindService(mConnection);
- } catch (IllegalArgumentException ie) {
- Log.e(TAG, "", ie);
- } finally {
- mService = null;
- }
- }
- }
+ mProfileConnector.connect(context, listener);
}
/** @hide */
@@ -214,16 +143,11 @@ public class BluetoothPbap implements BluetoothProfile {
* @hide
*/
public synchronized void close() {
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "", re);
- }
- }
- doUnbind();
- mServiceListener = null;
+ mProfileConnector.disconnect();
+ }
+
+ private IBluetoothPbap getService() {
+ return (IBluetoothPbap) mProfileConnector.getService();
}
/**
@@ -236,7 +160,7 @@ public class BluetoothPbap implements BluetoothProfile {
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
log("getConnectedDevices()");
- final IBluetoothPbap service = mService;
+ final IBluetoothPbap service = getService();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
@@ -265,7 +189,7 @@ public class BluetoothPbap implements BluetoothProfile {
public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
log("getConnectionState: device=" + device);
try {
- final IBluetoothPbap service = mService;
+ final IBluetoothPbap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
return service.getConnectionState(device, mAttributionSource);
}
@@ -289,7 +213,7 @@ public class BluetoothPbap implements BluetoothProfile {
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states));
- final IBluetoothPbap service = mService;
+ final IBluetoothPbap service = getService();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
@@ -330,7 +254,7 @@ public class BluetoothPbap implements BluetoothProfile {
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
try {
- final IBluetoothPbap service = mService;
+ final IBluetoothPbap service = getService();
if (service != null && isEnabled()
&& isValidDevice(device)) {
if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
@@ -359,7 +283,7 @@ public class BluetoothPbap implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
log("disconnect()");
- final IBluetoothPbap service = mService;
+ final IBluetoothPbap service = getService();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
return false;
@@ -373,25 +297,6 @@ public class BluetoothPbap implements BluetoothProfile {
return false;
}
- @SuppressLint("AndroidFrameworkBluetoothPermission")
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- log("Proxy object connected");
- mService = IBluetoothPbap.Stub.asInterface(service);
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.PBAP, BluetoothPbap.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- log("Proxy object disconnected");
- doUnbind();
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP);
- }
- }
- };
-
private boolean isEnabled() {
if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
return false;
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index cc91ad258d03..e096de8cb829 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -24,13 +26,15 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth PBAP Client Profile.
@@ -64,7 +68,7 @@ public final class BluetoothPbapClient implements BluetoothProfile {
"BluetoothPbapClient", IBluetoothPbapClient.class.getName()) {
@Override
public IBluetoothPbapClient getServiceInterface(IBinder service) {
- return IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothPbapClient.Stub.asInterface(service);
}
};
@@ -123,18 +127,20 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("connect(" + device + ") for PBAP Client.");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -155,19 +161,21 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("disconnect(" + device + ")" + new Exception());
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = true;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- service.disconnect(device, mAttributionSource);
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
return true;
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return false;
+ return defaultValue;
}
/**
@@ -184,19 +192,23 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("getConnectedDevices()");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -212,20 +224,23 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("getDevicesMatchingStates()");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -241,18 +256,20 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("getConnectionState(" + device + ")");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
- }
- }
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
private static void log(String msg) {
@@ -311,22 +328,22 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -370,17 +387,19 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("getConnectionPolicy(" + device + ")");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- }
- }
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
}
diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java
index ecd5e4077de9..a457679716d3 100644
--- a/core/java/android/bluetooth/BluetoothProfileConnector.java
+++ b/core/java/android/bluetooth/BluetoothProfileConnector.java
@@ -16,12 +16,16 @@
package android.bluetooth;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
@@ -29,6 +33,7 @@ import android.os.UserHandle;
import android.util.CloseGuard;
import android.util.Log;
+import java.util.List;
/**
* Connector for Bluetooth profile proxies to bind manager service and
* profile services
@@ -57,6 +62,30 @@ public abstract class BluetoothProfileConnector<T> {
}
};
+ private @Nullable ComponentName resolveSystemService(@NonNull Intent intent,
+ @NonNull PackageManager pm) {
+ List<ResolveInfo> results = pm.queryIntentServices(intent,
+ PackageManager.ResolveInfoFlags.of(0));
+ if (results == null) {
+ return null;
+ }
+ ComponentName comp = null;
+ for (int i = 0; i < results.size(); i++) {
+ ResolveInfo ri = results.get(i);
+ if ((ri.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ continue;
+ }
+ ComponentName foundComp = new ComponentName(ri.serviceInfo.applicationInfo.packageName,
+ ri.serviceInfo.name);
+ if (comp != null) {
+ throw new IllegalStateException("Multiple system services handle " + intent
+ + ": " + comp + ", " + foundComp);
+ }
+ comp = foundComp;
+ }
+ return comp;
+ }
+
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
logDebug("Proxy object connected");
@@ -99,8 +128,7 @@ public abstract class BluetoothProfileConnector<T> {
mCloseGuard.open("doUnbind");
try {
Intent intent = new Intent(mServiceName);
- ComponentName comp = intent.resolveSystemService(
- mContext.getPackageManager(), 0);
+ ComponentName comp = resolveSystemService(intent, mContext.getPackageManager());
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
UserHandle.CURRENT)) {
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index ab2b8eaaa36e..808fa3913316 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
@@ -26,14 +28,16 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth SIM
@@ -104,7 +108,7 @@ public final class BluetoothSap implements BluetoothProfile {
"BluetoothSap", IBluetoothSap.class.getName()) {
@Override
public IBluetoothSap getServiceInterface(IBinder service) {
- return IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothSap.Stub.asInterface(service);
}
};
@@ -155,17 +159,20 @@ public final class BluetoothSap implements BluetoothProfile {
public int getState() {
if (VDBG) log("getState()");
final IBluetoothSap service = getService();
- if (service != null) {
- try {
- return service.getState(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final int defaultValue = BluetoothSap.STATE_ERROR;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getState(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothSap.STATE_ERROR;
+ return defaultValue;
}
/**
@@ -180,18 +187,23 @@ public final class BluetoothSap implements BluetoothProfile {
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
final IBluetoothSap service = getService();
- if (service != null) {
+ final BluetoothDevice defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<BluetoothDevice> recv =
+ new SynchronousResultReceiver();
+ service.getClient(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getClient(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) log(Log.getStackTraceString(new Throwable()));
}
- return null;
+ return defaultValue;
}
/**
@@ -206,17 +218,20 @@ public final class BluetoothSap implements BluetoothProfile {
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
final IBluetoothSap service = getService();
- if (service != null) {
- try {
- return service.isConnected(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -244,16 +259,20 @@ public final class BluetoothSap implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothSap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -267,17 +286,23 @@ public final class BluetoothSap implements BluetoothProfile {
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothSap service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -291,18 +316,23 @@ public final class BluetoothSap implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothSap service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -316,16 +346,20 @@ public final class BluetoothSap implements BluetoothProfile {
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothSap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -370,20 +404,22 @@ public final class BluetoothSap implements BluetoothProfile {
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothSap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -425,16 +461,20 @@ public final class BluetoothSap implements BluetoothProfile {
public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothSap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothUtils.java b/core/java/android/bluetooth/BluetoothUtils.java
new file mode 100644
index 000000000000..867469241f84
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.bluetooth;
+
+import java.time.Duration;
+
+/**
+ * {@hide}
+ */
+public final class BluetoothUtils {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private BluetoothUtils() {}
+
+ /**
+ * Timeout value for synchronous binder call
+ */
+ private static final Duration SYNC_CALLS_TIMEOUT = Duration.ofSeconds(5);
+
+ /**
+ * @return timeout value for synchronous binder call
+ */
+ static Duration getSyncTimeout() {
+ return SYNC_CALLS_TIMEOUT;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothVolumeControl.java b/core/java/android/bluetooth/BluetoothVolumeControl.java
index ba83eca423f4..27532aabc3fc 100644
--- a/core/java/android/bluetooth/BluetoothVolumeControl.java
+++ b/core/java/android/bluetooth/BluetoothVolumeControl.java
@@ -17,6 +17,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -29,14 +31,16 @@ import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Bluetooth Volume Control service.
@@ -86,7 +90,7 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
IBluetoothVolumeControl.class.getName()) {
@Override
public IBluetoothVolumeControl getServiceInterface(IBinder service) {
- return IBluetoothVolumeControl.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothVolumeControl.Stub.asInterface(service);
}
};
@@ -134,17 +138,23 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -159,18 +169,23 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -185,16 +200,20 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -212,18 +231,19 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
})
public void setVolume(@Nullable BluetoothDevice device,
@IntRange(from = 0, to = 255) int volume) {
- if (DBG)
- log("setVolume(" + volume + ")");
+ if (DBG) log("setVolume(" + volume + ")");
final IBluetoothVolumeControl service = getService();
- try {
- if (service != null && isEnabled()) {
- service.setVolume(device, volume, mAttributionSource);
- return;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setVolume(device, volume, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null)
- Log.w(TAG, "Proxy not attached to service");
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
}
}
@@ -249,20 +269,22 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -285,16 +307,20 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index f913349e7955..540e5a778c27 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -514,16 +514,27 @@ public final class BluetoothLeScanner {
@Override
public void onScanResult(final ScanResult scanResult) {
Attributable.setAttributionSource(scanResult, mAttributionSource);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onScanResult() - mScannerId=" + mScannerId);
+ }
if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString());
// Check null in case the scan has been stopped
synchronized (this) {
- if (mScannerId <= 0) return;
+ if (mScannerId <= 0) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Ignoring result as scan stopped.");
+ }
+ return;
+ };
}
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onScanResult() - handler run");
+ }
mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
}
});
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 0d024b1d3200..bace45bccbf4 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -16,21 +16,32 @@
package android.companion.virtual;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.companion.AssociationInfo;
import android.content.Context;
import android.graphics.Point;
+import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
+import android.hardware.display.VirtualDisplayConfig;
import android.hardware.input.VirtualKeyboard;
import android.hardware.input.VirtualMouse;
import android.hardware.input.VirtualTouchscreen;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.view.Surface;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/**
* System level service for managing virtual devices.
@@ -44,6 +55,31 @@ public final class VirtualDeviceManager {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "VirtualDeviceManager";
+ /** @hide */
+ @IntDef(prefix = "DISPLAY_FLAG_",
+ flag = true,
+ value = {DISPLAY_FLAG_TRUSTED})
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ public @interface DisplayFlags {}
+
+ /**
+ * Indicates that the display is trusted to show system decorations and receive inputs without
+ * users' touch.
+ *
+ * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
+ * @hide // TODO(b/194949534): Unhide this API
+ */
+ public static final int DISPLAY_FLAG_TRUSTED = 1;
+
+ private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
+
private final IVirtualDeviceManager mService;
private final Context mContext;
@@ -93,6 +129,56 @@ public final class VirtualDeviceManager {
}
/**
+ * Creates a virtual display for this virtual device. All displays created on the same
+ * device belongs to the same display group.
+ *
+ * @param width The width of the virtual display in pixels, must be greater than 0.
+ * @param height The height of the virtual display in pixels, must be greater than 0.
+ * @param densityDpi The density of the virtual display in dpi, must be greater than 0.
+ * @param surface The surface to which the content of the virtual display should
+ * be rendered, or null if there is none initially. The surface can also be set later using
+ * {@link VirtualDisplay#setSurface(Surface)}.
+ * @param flags Either 0, or {@link #DISPLAY_FLAG_TRUSTED}.
+ * @param callback Callback to call when the state of the {@link VirtualDisplay} changes
+ * @param handler The handler on which the listener should be invoked, or null
+ * if the listener should be invoked on the calling thread's looper.
+ * @return The newly created virtual display, or {@code null} if the application could
+ * not create the virtual display.
+ *
+ * @see DisplayManager#createVirtualDisplay
+ * @hide
+ */
+ // TODO(b/194949534): Unhide this API
+ // Suppress "ExecutorRegistration" because DisplayManager.createVirtualDisplay takes a
+ // handler
+ @SuppressLint("ExecutorRegistration")
+ @Nullable
+ public VirtualDisplay createVirtualDisplay(
+ int width,
+ int height,
+ int densityDpi,
+ @Nullable Surface surface,
+ @DisplayFlags int flags,
+ @Nullable Handler handler,
+ @Nullable VirtualDisplay.Callback callback) {
+ // TODO(b/205343547): Handle display groups properly instead of creating a new display
+ // group for every new virtual display created using this API.
+ // belongs to the same display group.
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ // DisplayManager will call into VirtualDeviceManagerInternal to register the
+ // created displays.
+ return displayManager.createVirtualDisplay(
+ mVirtualDevice,
+ new VirtualDisplayConfig.Builder(
+ getVirtualDisplayName(), width, height, densityDpi)
+ .setSurface(surface)
+ .setFlags(getVirtualDisplayFlags(flags))
+ .build(),
+ callback,
+ handler);
+ }
+
+ /**
* Closes the virtual device, stopping and tearing down any virtual displays,
* audio policies, and event injection that's currently in progress.
*/
@@ -186,5 +272,25 @@ public final class VirtualDeviceManager {
throw e.rethrowFromSystemServer();
}
}
+
+ private int getVirtualDisplayFlags(@DisplayFlags int flags) {
+ int virtualDisplayFlags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
+ if ((flags & DISPLAY_FLAG_TRUSTED) != 0) {
+ virtualDisplayFlags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
+ }
+ return virtualDisplayFlags;
+ }
+
+ private String getVirtualDisplayName() {
+ try {
+ // Currently this just use the association ID, which means all of the virtual
+ // displays created using the same virtual device will have the same name. The name
+ // should only be used for informational purposes, and not for identifying the
+ // display in code.
+ return "VirtualDevice_" + mVirtualDevice.getAssociationId();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
}
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 6ae2bb5b642a..157e709a67f0 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -22,6 +22,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityThread;
+import android.app.AppGlobals;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
@@ -191,10 +192,42 @@ public final class AttributionSource implements Parcelable {
return new ScopedParcelState(this);
}
- /** @hide */
- public static AttributionSource myAttributionSource() {
- return new AttributionSource(Process.myUid(), ActivityThread.currentOpPackageName(),
- /*attributionTag*/ null, (String[]) /*renouncedPermissions*/ null, /*next*/ null);
+ /**
+ * Returns a generic {@link AttributionSource} that represents the entire
+ * calling process.
+ *
+ * <p>Callers are <em>strongly</em> encouraged to use a more specific
+ * attribution source whenever possible, such as from
+ * {@link Context#getAttributionSource()}, since that enables developers to
+ * have more detailed and scoped control over attribution within
+ * sub-components of their app.
+ *
+ * @see Context#createAttributionContext(String)
+ * @see Context#getAttributionTag()
+ * @return a generic {@link AttributionSource} representing the entire
+ * calling process
+ * @throws IllegalStateException when no accurate {@link AttributionSource}
+ * can be determined
+ */
+ public static @NonNull AttributionSource myAttributionSource() {
+
+ final AttributionSource globalSource = ActivityThread.currentAttributionSource();
+ if (globalSource != null) {
+ return globalSource;
+ }
+
+ int uid = Process.myUid();
+ if (uid == Process.ROOT_UID) {
+ uid = Process.SYSTEM_UID;
+ }
+ try {
+ return new AttributionSource.Builder(uid)
+ .setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0])
+ .build();
+ } catch (Exception ignored) {
+ }
+
+ throw new IllegalStateException("Failed to resolve AttributionSource");
}
/**
@@ -247,7 +280,7 @@ public final class AttributionSource implements Parcelable {
* whether the attribution source is one for the calling app to prevent the caller
* to pass you a source from another app without including themselves in the
* attribution chain.
- *f
+ *
* @return if the attribution source cannot be trusted to be from the caller.
*/
public boolean checkCallingUid() {
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index f335ae483bf7..d46a0c67341f 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -392,7 +392,7 @@ public abstract class BroadcastReceiver {
PendingResult res = mPendingResult;
mPendingResult = null;
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ if (res != null && Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
res.mReceiverClassName = getClass().getName();
Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"BroadcastReceiver#goAsync#ClassName:" + res.mReceiverClassName,
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 518e7534d512..cc3c01241c66 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -109,7 +109,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
mAuthority = authority;
mStable = stable;
- mCloseGuard.open("close");
+ mCloseGuard.open("ContentProviderClient.close");
}
/**
@@ -695,7 +695,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
CursorWrapperInner(Cursor cursor) {
super(cursor);
- mCloseGuard.open("close");
+ mCloseGuard.open("CursorWrapperInner.close");
}
@Override
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 184acb1a81ef..01d231c51751 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -3858,7 +3858,7 @@ public abstract class ContentResolver implements ContentInterface {
CursorWrapperInner(Cursor cursor, IContentProvider contentProvider) {
super(cursor);
mContentProvider = contentProvider;
- mCloseGuard.open("close");
+ mCloseGuard.open("CursorWrapperInner.close");
}
@Override
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 543239bef101..6d13712b68ae 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4790,6 +4790,15 @@ public abstract class Context {
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.view.selectiontoolbar.SelectionToolbarManager} for selection toolbar service.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ public static final String SELECTION_TOOLBAR_SERVICE = "selection_toolbar";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.graphics.fonts.FontManager} for font services.
*
* @see #getSystemService(String)
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 983d0cc4b088..2ff29cbdcc2a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5598,7 +5598,9 @@ public class Intent implements Parcelable, Cloneable {
*
* <p>Targets provided in this way will be presented inline with all other targets provided
* by services from other apps. They will be prioritized before other service targets, but
- * after those targets provided by sources that the user has manually pinned to the front.</p>
+ * after those targets provided by sources that the user has manually pinned to the front.
+ * You can provide up to two targets on this extra (the limit of two targets
+ * starts in Android 10).</p>
*
* @see #ACTION_CHOOSER
*/
@@ -5709,9 +5711,11 @@ public class Intent implements Parcelable, Cloneable {
/**
* A Parcelable[] of {@link Intent} or
* {@link android.content.pm.LabeledIntent} objects as set with
- * {@link #putExtra(String, Parcelable[])} of additional activities to place
- * a the front of the list of choices, when shown to the user with a
- * {@link #ACTION_CHOOSER}.
+ * {@link #putExtra(String, Parcelable[])} to place
+ * at the front of the list of choices, when shown to the user with an
+ * {@link #ACTION_CHOOSER}. You can choose up to two additional activities
+ * to show before the app suggestions (the limit of two additional activities starts in
+ * Android 10).
*/
public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
@@ -5915,6 +5919,14 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_UID = "android.intent.extra.UID";
/**
+ * Used as an optional int extra field in {@link android.content.Intent#ACTION_PACKAGE_ADDED}
+ * intents to supply the previous uid the package had been assigned.
+ * This would only be set when a package is leaving sharedUserId in an upgrade, or when a
+ * system app upgrade that had left sharedUserId is getting uninstalled.
+ */
+ public static final String EXTRA_PREVIOUS_UID = "android.intent.extra.PREVIOUS_UID";
+
+ /**
* @hide String array of package names.
*/
@SystemApi
@@ -5946,6 +5958,16 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING";
/**
+ * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED},
+ * {@link android.content.Intent#ACTION_UID_REMOVED}, and
+ * {@link android.content.Intent#ACTION_PACKAGE_ADDED}
+ * intents to indicate that this package is changing its UID.
+ * This would only be set when a package is leaving sharedUserId in an upgrade, or when a
+ * system app upgrade that had left sharedUserId is getting uninstalled.
+ */
+ public static final String EXTRA_UID_CHANGING = "android.intent.extra.UID_CHANGING";
+
+ /**
* Used as an int extra field in {@link android.app.AlarmManager} pending intents
* to tell the application being invoked how many pending alarms are being
* delivered with the intent. For one-shot alarms this will always be 1.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f984f4362f37..973fe015f9ac 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3162,6 +3162,8 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has a CDMA telephony stack.
+ *
+ * <p>This feature should only be defined if {@link #FEATURE_TELEPHONY} has been defined.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
@@ -3169,6 +3171,8 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has a GSM telephony stack.
+ *
+ * <p>This feature should only be defined if {@link #FEATURE_TELEPHONY} has been defined.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
@@ -3180,6 +3184,9 @@ public abstract class PackageManager {
* <p>Devices declaring this feature must have an implementation of the
* {@link android.telephony.TelephonyManager#getAllowedCarriers} and
* {@link android.telephony.TelephonyManager#setAllowedCarriers}.
+ *
+ * This feature should only be defined if {@link #FEATURE_TELEPHONY_SUBSCRIPTION}
+ * has been defined.
* @hide
*/
@SystemApi
@@ -3190,6 +3197,9 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
* supports embedded subscriptions on eUICCs.
+ *
+ * This feature should only be defined if {@link #FEATURE_TELEPHONY_SUBSCRIPTION}
+ * has been defined.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
@@ -3197,6 +3207,9 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
* supports cell-broadcast reception using the MBMS APIs.
+ *
+ * <p>This feature should only be defined if both {@link #FEATURE_TELEPHONY_SUBSCRIPTION}
+ * and {@link #FEATURE_TELEPHONY_RADIO_ACCESS} have been defined.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms";
@@ -3204,6 +3217,8 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
* supports attaching to IMS implementations using the ImsService API in telephony.
+ *
+ * <p>This feature should only be defined if {@link #FEATURE_TELEPHONY_DATA} has been defined.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
@@ -3240,6 +3255,62 @@ public abstract class PackageManager {
"android.hardware.telephony.ims.singlereg";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports Telecom Service APIs.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELECOM = "android.software.telecom";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports Telephony APIs for calling service.
+ *
+ * <p>This feature should only be defined if {@link #FEATURE_TELEPHONY_RADIO_ACCESS},
+ * {@link #FEATURE_TELEPHONY_SUBSCRIPTION}, and {@link #FEATURE_TELECOM} have been defined.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_CALLING = "android.hardware.telephony.calling";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports Telephony APIs for data service.
+ *
+ * <p>This feature should only be defined if both {@link #FEATURE_TELEPHONY_SUBSCRIPTION}
+ * and {@link #FEATURE_TELEPHONY_RADIO_ACCESS} have been defined.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_DATA = "android.hardware.telephony.data";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports Telephony APIs for SMS and MMS.
+ *
+ * <p>This feature should only be defined if both {@link #FEATURE_TELEPHONY_SUBSCRIPTION}
+ * and {@link #FEATURE_TELEPHONY_RADIO_ACCESS} have been defined.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_MESSAGING = "android.hardware.telephony.messaging";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports Telephony APIs for the radio access.
+ *
+ * <p>This feature should only be defined if {@link #FEATURE_TELEPHONY} has been defined.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports Telephony APIs for the subscription.
+ *
+ * <p>This feature should only be defined if {@link #FEATURE_TELEPHONY} has been defined.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_SUBSCRIPTION =
+ "android.hardware.telephony.subscription";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device is capable of communicating with
* other devices via ultra wideband.
@@ -3280,7 +3351,9 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The Connection Service API is enabled on the device.
+ * @deprecated use {@link #FEATURE_TELECOM} instead.
*/
+ @Deprecated
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 8ebb8ecd4c06..01bf49eb03a6 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -2432,27 +2432,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration
break;
}
- switch (config.uiMode & Configuration.UI_MODE_TYPE_MASK) {
- case Configuration.UI_MODE_TYPE_APPLIANCE:
- parts.add("appliance");
- break;
- case Configuration.UI_MODE_TYPE_DESK:
- parts.add("desk");
- break;
- case Configuration.UI_MODE_TYPE_TELEVISION:
- parts.add("television");
- break;
- case Configuration.UI_MODE_TYPE_CAR:
- parts.add("car");
- break;
- case Configuration.UI_MODE_TYPE_WATCH:
- parts.add("watch");
- break;
- case Configuration.UI_MODE_TYPE_VR_HEADSET:
- parts.add("vrheadset");
- break;
- default:
- break;
+ final String uiModeTypeString =
+ getUiModeTypeString(config.uiMode & Configuration.UI_MODE_TYPE_MASK);
+ if (uiModeTypeString != null) {
+ parts.add(uiModeTypeString);
}
switch (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) {
@@ -2587,6 +2570,28 @@ public final class Configuration implements Parcelable, Comparable<Configuration
}
/**
+ * @hide
+ */
+ public static String getUiModeTypeString(int uiModeType) {
+ switch (uiModeType) {
+ case Configuration.UI_MODE_TYPE_APPLIANCE:
+ return "appliance";
+ case Configuration.UI_MODE_TYPE_DESK:
+ return "desk";
+ case Configuration.UI_MODE_TYPE_TELEVISION:
+ return "television";
+ case Configuration.UI_MODE_TYPE_CAR:
+ return "car";
+ case Configuration.UI_MODE_TYPE_WATCH:
+ return "watch";
+ case Configuration.UI_MODE_TYPE_VR_HEADSET:
+ return "vrheadset";
+ default:
+ return null;
+ }
+ }
+
+ /**
* Generate a delta Configuration between <code>base</code> and <code>change</code>. The
* resulting delta can be used with {@link #updateFrom(Configuration)}.
* <p />
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index cf25c3c56208..69d573f84975 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -224,7 +224,7 @@ public abstract class AbstractCursor implements CrossProcessCursor {
/* Implementation */
public AbstractCursor() {
mPos = -1;
- mCloseGuard.open("close");
+ mCloseGuard.open("AbstractCursor.close");
}
@Override
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index ccb7cf19d0b1..f13c79587a28 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -142,7 +142,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
if (mWindowPtr == 0) {
throw new AssertionError(); // Not possible, the native code won't return it.
}
- mCloseGuard.open("close");
+ mCloseGuard.open("CursorWindow.close");
}
/**
@@ -170,7 +170,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
throw new AssertionError(); // Not possible, the native code won't return it.
}
mName = nativeGetName(mWindowPtr);
- mCloseGuard.open("close");
+ mCloseGuard.open("CursorWindow.close");
}
@Override
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 328858b260ac..6d6ec06182d6 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -179,7 +179,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
mIsReadOnlyConnection = mConfiguration.isReadOnlyDatabase();
mPreparedStatementCache = new PreparedStatementCache(
mConfiguration.maxSqlCacheSize);
- mCloseGuard.open("close");
+ mCloseGuard.open("SQLiteConnection.close");
}
@Override
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index d3ad6bb27b3c..216c9c26424d 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -218,7 +218,7 @@ public final class SQLiteConnectionPool implements Closeable {
// Mark the pool as being open for business.
mIsOpen = true;
- mCloseGuard.open("close");
+ mCloseGuard.open("SQLiteConnectionPool.close");
}
/**
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index a4a8f313e3ba..4683d252b68a 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -253,7 +253,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
NativeAllocationRegistry registry = new NativeAllocationRegistry(
loader, nGetNativeFinalizer(), bufferSize);
mCleaner = registry.registerNativeAllocation(this, mNativeObject);
- mCloseGuard.open("close");
+ mCloseGuard.open("HardwareBuffer.close");
}
@Override
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index e9fffa30ae57..282f1d343959 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -687,7 +687,7 @@ public class SystemSensorManager extends SensorManager {
new WeakReference<>(this), looper.getQueue(),
packageName, mode, manager.mContext.getOpPackageName(),
manager.mContext.getAttributionTag());
- mCloseGuard.open("dispose");
+ mCloseGuard.open("BaseEventQueue.dispose");
mManager = manager;
}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 19fb845d9384..2ac194b67192 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -144,4 +144,6 @@ interface IInputManager {
void openLightSession(int deviceId, String opPkg, in IBinder token);
void closeLightSession(int deviceId, in IBinder token);
+
+ void cancelCurrentTouch();
}
diff --git a/core/java/android/hardware/input/InputDeviceLightsManager.java b/core/java/android/hardware/input/InputDeviceLightsManager.java
index 885df7be2510..802e6dde497a 100644
--- a/core/java/android/hardware/input/InputDeviceLightsManager.java
+++ b/core/java/android/hardware/input/InputDeviceLightsManager.java
@@ -100,7 +100,7 @@ class InputDeviceLightsManager extends LightsManager {
* Instantiated by {@link LightsManager#openSession()}.
*/
private InputDeviceLightsSession() {
- mCloseGuard.open("close");
+ mCloseGuard.open("InputDeviceLightsSession.close");
}
/**
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6f0c944b76ff..6253fb90323a 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1650,6 +1650,18 @@ public final class InputManager {
}
/**
+ * Cancel all ongoing pointer gestures on all displays.
+ * @hide
+ */
+ public void cancelCurrentTouch() {
+ try {
+ mIm.cancelCurrentTouch();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Listens for changes in input devices.
*/
public interface InputDeviceListener {
diff --git a/core/java/android/hardware/lights/SystemLightsManager.java b/core/java/android/hardware/lights/SystemLightsManager.java
index d0df611e7842..055a7f43f9ed 100644
--- a/core/java/android/hardware/lights/SystemLightsManager.java
+++ b/core/java/android/hardware/lights/SystemLightsManager.java
@@ -145,7 +145,7 @@ public final class SystemLightsManager extends LightsManager {
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
private SystemLightsSession() {
- mCloseGuard.open("close");
+ mCloseGuard.open("SystemLightsSession.close");
}
/**
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index a525f58371f5..3b50f0faf45b 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -69,7 +69,7 @@ public class ContextHubClient implements Closeable {
mCloseGuard = null;
} else {
mCloseGuard = CloseGuard.get();
- mCloseGuard.open("close");
+ mCloseGuard.open("ContextHubClient.close");
}
}
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 1c35cb66ada8..60d8cacd19be 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -69,7 +69,7 @@ public class UsbDeviceConnection {
boolean wasOpened = native_open(name, pfd.getFileDescriptor());
if (wasOpened) {
- mCloseGuard.open("close");
+ mCloseGuard.open("UsbDeviceConnection.close");
}
return wasOpened;
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index d1c6465d62c8..6ac5e8de8fa7 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -103,7 +103,7 @@ public class UsbRequest {
endpoint.getAttributes(), endpoint.getMaxPacketSize(), endpoint.getInterval());
if (wasInitialized) {
- mCloseGuard.open("close");
+ mCloseGuard.open("UsbRequest.close");
}
return wasInitialized;
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index f33a035673b8..a6ab1381aa57 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -18,8 +18,11 @@ package android.net;
import static android.net.NetworkStats.METERED_ALL;
import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkTemplate.MATCH_BLUETOOTH;
import static android.net.NetworkTemplate.MATCH_CARRIER;
+import static android.net.NetworkTemplate.MATCH_ETHERNET;
import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT;
import android.annotation.NonNull;
@@ -324,7 +327,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
@NonNull
private byte[] getNetworkTemplateBytesForBackup() throws IOException {
- if (!template.isPersistable()) {
+ if (!isTemplatePersistable(this.template)) {
Log.wtf(TAG, "Trying to backup non-persistable template: " + this);
}
@@ -378,4 +381,28 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
"Restored network template contains unknown match rule " + matchRule, e);
}
}
+
+ /**
+ * Check if the template can be persisted into disk.
+ */
+ public static boolean isTemplatePersistable(@NonNull NetworkTemplate template) {
+ switch (template.getMatchRule()) {
+ case MATCH_BLUETOOTH:
+ case MATCH_ETHERNET:
+ return true;
+ case MATCH_CARRIER:
+ case MATCH_MOBILE:
+ return !template.getSubscriberIds().isEmpty();
+ case MATCH_WIFI:
+ if (Objects.equals(template.getWifiNetworkKey(), null)
+ && template.getSubscriberIds().isEmpty()) {
+ return false;
+ }
+ return true;
+ default:
+ // Don't allow persistable for unknown types or legacy types such as
+ // MATCH_MOBILE_WILDCARD, MATCH_PROXY, etc.
+ return false;
+ }
+ }
}
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index ba9332dece26..c6071959f438 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -161,6 +161,7 @@ public abstract class BatteryConsumer {
*/
@IntDef(prefix = {"PROCESS_STATE_"}, value = {
PROCESS_STATE_ANY,
+ PROCESS_STATE_UNSPECIFIED,
PROCESS_STATE_FOREGROUND,
PROCESS_STATE_BACKGROUND,
PROCESS_STATE_FOREGROUND_SERVICE,
@@ -169,7 +170,8 @@ public abstract class BatteryConsumer {
public @interface ProcessState {
}
- public static final int PROCESS_STATE_ANY = 0;
+ public static final int PROCESS_STATE_UNSPECIFIED = 0;
+ public static final int PROCESS_STATE_ANY = PROCESS_STATE_UNSPECIFIED;
public static final int PROCESS_STATE_FOREGROUND = 1;
public static final int PROCESS_STATE_BACKGROUND = 2;
public static final int PROCESS_STATE_FOREGROUND_SERVICE = 3;
@@ -180,7 +182,7 @@ public abstract class BatteryConsumer {
static {
// Assign individually to avoid future mismatch
- sProcessStateNames[PROCESS_STATE_ANY] = "any";
+ sProcessStateNames[PROCESS_STATE_UNSPECIFIED] = "unspecified";
sProcessStateNames[PROCESS_STATE_FOREGROUND] = "fg";
sProcessStateNames[PROCESS_STATE_BACKGROUND] = "bg";
sProcessStateNames[PROCESS_STATE_FOREGROUND_SERVICE] = "fgs";
@@ -188,6 +190,7 @@ public abstract class BatteryConsumer {
private static final int[] SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = {
POWER_COMPONENT_CPU,
+ POWER_COMPONENT_MOBILE_RADIO,
};
static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
@@ -213,7 +216,7 @@ public abstract class BatteryConsumer {
sb.append("powerComponent=").append(sPowerComponentNames[powerComponent]);
dimensionSpecified = true;
}
- if (processState != PROCESS_STATE_ANY) {
+ if (processState != PROCESS_STATE_UNSPECIFIED) {
if (dimensionSpecified) {
sb.append(", ");
}
@@ -283,7 +286,7 @@ public abstract class BatteryConsumer {
if (mShortString == null) {
StringBuilder sb = new StringBuilder();
sb.append(powerComponentIdToString(powerComponent));
- if (processState != PROCESS_STATE_ANY) {
+ if (processState != PROCESS_STATE_UNSPECIFIED) {
sb.append(':');
sb.append(processStateToString(processState));
}
@@ -333,7 +336,7 @@ public abstract class BatteryConsumer {
* for all values of other dimensions such as process state.
*/
public Key getKey(@PowerComponent int componentId) {
- return mData.getKey(componentId, PROCESS_STATE_ANY);
+ return mData.getKey(componentId, PROCESS_STATE_UNSPECIFIED);
}
/**
@@ -352,7 +355,7 @@ public abstract class BatteryConsumer {
*/
public double getConsumedPower(@PowerComponent int componentId) {
return mPowerComponents.getConsumedPower(
- mData.getKeyOrThrow(componentId, PROCESS_STATE_ANY));
+ mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED));
}
/**
@@ -374,7 +377,7 @@ public abstract class BatteryConsumer {
*/
public @PowerModel int getPowerModel(@BatteryConsumer.PowerComponent int componentId) {
return mPowerComponents.getPowerModel(
- mData.getKeyOrThrow(componentId, PROCESS_STATE_ANY));
+ mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED));
}
/**
@@ -706,7 +709,7 @@ public abstract class BatteryConsumer {
if (isSupported) {
for (int processState = 0; processState < PROCESS_STATE_COUNT;
processState++) {
- if (processState == PROCESS_STATE_ANY) {
+ if (processState == PROCESS_STATE_UNSPECIFIED) {
continue;
}
@@ -789,7 +792,7 @@ public abstract class BatteryConsumer {
@NonNull
public T setConsumedPower(@PowerComponent int componentId, double componentPower,
@PowerModel int powerModel) {
- mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_ANY),
+ mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
componentPower, powerModel);
return (T) this;
}
@@ -825,8 +828,9 @@ public abstract class BatteryConsumer {
@NonNull
public T setUsageDurationMillis(@UidBatteryConsumer.PowerComponent int componentId,
long componentUsageTimeMillis) {
- mPowerComponentsBuilder.setUsageDurationMillis(getKey(componentId, PROCESS_STATE_ANY),
- componentUsageTimeMillis);
+ mPowerComponentsBuilder
+ .setUsageDurationMillis(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
+ componentUsageTimeMillis);
return (T) this;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 584f3c43911b..fa209cc02cd1 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -669,7 +669,7 @@ public abstract class BatteryStats implements Parcelable {
case BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE:
return BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
default:
- return BatteryConsumer.PROCESS_STATE_ANY;
+ return BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
}
}
@@ -963,6 +963,13 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getNetworkActivityPackets(int type, int which);
@UnsupportedAppUsage
public abstract long getMobileRadioActiveTime(int which);
+
+ /**
+ * Returns the amount of time (in microseconds) this UID was in the specified processState.
+ */
+ public abstract long getMobileRadioActiveTimeInProcessState(
+ @BatteryConsumer.ProcessState int processState);
+
public abstract int getMobileRadioActiveCount(int which);
/**
@@ -1061,6 +1068,16 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getMobileRadioMeasuredBatteryConsumptionUC();
/**
+ * Returns the battery consumption (in microcoulombs) of the uid's radio usage when in the
+ * specified process state.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getMobileRadioMeasuredBatteryConsumptionUC(
+ @BatteryConsumer.ProcessState int processState);
+
+ /**
* Returns the battery consumption (in microcoulombs) of the screen while on and uid active,
* derived from on device power measurement data.
* Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index ed44fb62ee79..a23dae80625d 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -556,7 +556,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
}
String label = BatteryConsumer.powerComponentIdToString(componentId);
- if (key.processState != BatteryConsumer.PROCESS_STATE_ANY) {
+ if (key.processState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
label = label
+ "(" + BatteryConsumer.processStateToString(key.processState) + ")";
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index e2d7847b85e9..429450ce8e5e 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -1308,10 +1308,11 @@ public class Binder implements IBinder {
data.readCallingWorkSourceUid());
observer.callEnded(callSession, data.dataSize(), reply.dataSize(), workSourceUid);
}
+
+ checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
+ reply.recycle();
+ data.recycle();
}
- checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
- reply.recycle();
- data.recycle();
// Just in case -- we are done with the IPC, so there should be no more strict
// mode violations that have gathered for this thread. Either they have been
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index e8631119bbc6..590494cd3273 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -18,6 +18,7 @@ package android.os;
import static android.os.BatteryConsumer.POWER_COMPONENT_ANY;
import static android.os.BatteryConsumer.POWER_COMPONENT_COUNT;
import static android.os.BatteryConsumer.PROCESS_STATE_ANY;
+import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
import static android.os.BatteryConsumer.convertMahToDeciCoulombs;
import android.annotation.NonNull;
@@ -339,7 +340,7 @@ class PowerComponents {
serializer.startTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId);
- if (key.processState != PROCESS_STATE_ANY) {
+ if (key.processState != PROCESS_STATE_UNSPECIFIED) {
serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_PROCESS_STATE,
key.processState);
}
@@ -398,7 +399,7 @@ class PowerComponents {
switch (parser.getName()) {
case BatteryUsageStats.XML_TAG_COMPONENT: {
int componentId = -1;
- int processState = PROCESS_STATE_ANY;
+ int processState = PROCESS_STATE_UNSPECIFIED;
double powerMah = 0;
long durationMs = 0;
int model = BatteryConsumer.POWER_MODEL_UNDEFINED;
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index d0d6cb76280a..003776175f26 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -396,7 +396,7 @@ public class SystemVibrator extends Vibrator {
mExecutor.execute(() -> {
boolean anyVibrating;
synchronized (mLock) {
- int allInitializedMask = 1 << mVibratorListeners.size() - 1;
+ int allInitializedMask = (1 << mVibratorListeners.size()) - 1;
int vibratorMask = 1 << vibratorIdx;
if ((mInitializedMask & vibratorMask) == 0) {
// First state report for this vibrator, set vibrating initial value.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 563d6cc3e07f..b3639e419a56 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1546,6 +1546,17 @@ public class UserManager {
private static final String ACTION_CREATE_USER = "android.os.action.CREATE_USER";
/**
+ * Action to start an activity to create a supervised user.
+ * Only devices with non-empty config_supervisedUserCreationPackage support this.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
+ public static final String ACTION_CREATE_SUPERVISED_USER =
+ "android.os.action.CREATE_SUPERVISED_USER";
+
+ /**
* Extra containing a name for the user being created. Optional parameter passed to
* ACTION_CREATE_USER activity.
* @hide
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index adf7955cedb1..5e4057bd840d 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1437,11 +1437,36 @@ public class StorageManager {
throw new IllegalStateException("Missing primary storage");
}
- private static final int DEFAULT_THRESHOLD_PERCENTAGE = 5;
- private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500);
+ /**
+ * Devices having above STORAGE_THRESHOLD_PERCENT_HIGH of total space free are considered to be
+ * in high free space category.
+ *
+ * @hide
+ */
+ public static final int STORAGE_THRESHOLD_PERCENT_HIGH = 20;
+ /**
+ * Devices having below STORAGE_THRESHOLD_PERCENT_LOW of total space free are considered to be
+ * in low free space category.
+ *
+ * @hide
+ */
+ public static final int STORAGE_THRESHOLD_PERCENT_LOW = 5;
+ /**
+ * For devices in high free space category, CACHE_RESERVE_PERCENT_HIGH percent of total space is
+ * allocated for cache.
+ *
+ * @hide
+ */
+ public static final int CACHE_RESERVE_PERCENT_HIGH = 10;
+ /**
+ * For devices in low free space category, CACHE_RESERVE_PERCENT_LOW percent of total space is
+ * allocated for cache.
+ *
+ * @hide
+ */
+ public static final int CACHE_RESERVE_PERCENT_LOW = 2;
- private static final int DEFAULT_CACHE_PERCENTAGE = 10;
- private static final long DEFAULT_CACHE_MAX_BYTES = DataUnit.GIBIBYTES.toBytes(5);
+ private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500);
private static final long DEFAULT_FULL_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(1);
@@ -1465,7 +1490,7 @@ public class StorageManager {
@UnsupportedAppUsage
public long getStorageLowBytes(File path) {
final long lowPercent = Settings.Global.getInt(mResolver,
- Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
+ Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, STORAGE_THRESHOLD_PERCENT_LOW);
final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
final long maxLowBytes = Settings.Global.getLong(mResolver,
@@ -1475,28 +1500,54 @@ public class StorageManager {
}
/**
+ * Compute the minimum number of bytes of storage on the device that could
+ * be reserved for cached data depending on the device state which is then passed on
+ * to getStorageCacheBytes.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ @SuppressLint("StreamFiles")
+ public long computeStorageCacheBytes(@NonNull File path) {
+ final long totalBytes = path.getTotalSpace();
+ final long usableBytes = path.getUsableSpace();
+ final long storageThresholdHighBytes = totalBytes * STORAGE_THRESHOLD_PERCENT_HIGH / 100;
+ final long storageThresholdLowBytes = getStorageLowBytes(path);
+ long result;
+ if (usableBytes > storageThresholdHighBytes) {
+ // If free space is >STORAGE_THRESHOLD_PERCENT_HIGH of total space,
+ // reserve CACHE_RESERVE_PERCENT_HIGH of total space
+ result = totalBytes * CACHE_RESERVE_PERCENT_HIGH / 100;
+ } else if (usableBytes < storageThresholdLowBytes) {
+ // If free space is <min(STORAGE_THRESHOLD_PERCENT_LOW of total space, 500MB),
+ // reserve CACHE_RESERVE_PERCENT_LOW of total space
+ result = totalBytes * CACHE_RESERVE_PERCENT_LOW / 100;
+ } else {
+ // Else, linearly interpolate the amount of space to reserve
+ result = ((CACHE_RESERVE_PERCENT_HIGH - CACHE_RESERVE_PERCENT_LOW)
+ * (usableBytes - storageThresholdHighBytes) + CACHE_RESERVE_PERCENT_HIGH
+ * (storageThresholdHighBytes - storageThresholdLowBytes)) * totalBytes
+ / (100 * (storageThresholdHighBytes - storageThresholdLowBytes));
+ }
+ return result;
+ }
+
+ /**
* Return the minimum number of bytes of storage on the device that should
* be reserved for cached data.
*
* @hide
*/
- public long getStorageCacheBytes(File path, @AllocateFlags int flags) {
- final long cachePercent = Settings.Global.getInt(mResolver,
- Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE, DEFAULT_CACHE_PERCENTAGE);
- final long cacheBytes = (path.getTotalSpace() * cachePercent) / 100;
-
- final long maxCacheBytes = Settings.Global.getLong(mResolver,
- Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES, DEFAULT_CACHE_MAX_BYTES);
-
- final long result = Math.min(cacheBytes, maxCacheBytes);
+ public long getStorageCacheBytes(@NonNull File path, @AllocateFlags int flags) {
if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) {
return 0;
} else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED) != 0) {
return 0;
} else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_HALF_RESERVED) != 0) {
- return result / 2;
+ return computeStorageCacheBytes(path) / 2;
} else {
- return result;
+ return computeStorageCacheBytes(path);
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7228b5ab7feb..edacffca494f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9327,16 +9327,6 @@ public final class Settings {
"emergency_gesture_sound_enabled";
/**
- * The power button "cooldown" period in milliseconds after the Emergency gesture is
- * triggered, during which single-key actions on the power button are suppressed. Cooldown
- * period is disabled if set to zero.
- *
- * @hide
- */
- public static final String EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS =
- "emergency_gesture_power_button_cooldown_period_ms";
-
- /**
* Whether the camera launch gesture to double tap the power button when the screen is off
* should be disabled.
*
@@ -13925,6 +13915,16 @@ public final class Settings {
public static final String EMERGENCY_AFFORDANCE_NEEDED = "emergency_affordance_needed";
/**
+ * The power button "cooldown" period in milliseconds after the Emergency gesture is
+ * triggered, during which single-key actions on the power button are suppressed. Cooldown
+ * period is disabled if set to zero.
+ *
+ * @hide
+ */
+ public static final String EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS =
+ "emergency_gesture_power_button_cooldown_period_ms";
+
+ /**
* Whether to enable automatic system server heap dumps. This only works on userdebug or
* eng builds, not on user builds. This is set by the user and overrides the config value.
* 1 means enable, 0 means disable.
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 50a44a1bb08d..e3c396976f6c 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5317,6 +5317,13 @@ public final class Telephony {
public static final String COLUMN_PROFILE_CLASS = "profile_class";
/**
+ * TelephonyProvider column name for the port index of the active UICC port.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String COLUMN_PORT_INDEX = "port_index";
+
+ /**
* A testing profile can be pre-loaded or downloaded onto
* the eUICC and provides connectivity to test equipment
* for the purpose of testing the device and the eUICC. It
@@ -5446,5 +5453,12 @@ public final class Telephony {
*/
public static final String COLUMN_PHONE_NUMBER_SOURCE_IMS =
"phone_number_source_ims";
+
+ /**
+ * TelephonyProvider column name for the device's preferred usage setting.
+ *
+ * @hide
+ */
+ public static final String COLUMN_USAGE_SETTING = "usage_setting";
}
}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 3b4f7e241852..f90055829b89 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -317,16 +317,35 @@ public final class KeymasterDefs {
ErrorCode.MISSING_MIN_MAC_LENGTH; // -58;
public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH =
ErrorCode.UNSUPPORTED_MIN_MAC_LENGTH; // -59;
+ public static final int KM_ERROR_UNSUPPORTED_KDF = ErrorCode.UNSUPPORTED_KDF; // -60
+ public static final int KM_ERROR_UNSUPPORTED_EC_CURVE = ErrorCode.UNSUPPORTED_EC_CURVE; // -61
+ // -62 is KEY_REQUIRES_UPGRADE and is handled by Keystore.
+ public static final int KM_ERROR_ATTESTATION_CHALLENGE_MISSING =
+ ErrorCode.ATTESTATION_CHALLENGE_MISSING; // -63
+ public static final int KM_ERROR_KEYMINT_NOT_CONFIGURED =
+ ErrorCode.KEYMINT_NOT_CONFIGURED; // -64
+ public static final int KM_ERROR_ATTESTATION_APPLICATION_ID_MISSING =
+ ErrorCode.ATTESTATION_APPLICATION_ID_MISSING; // -65;
public static final int KM_ERROR_CANNOT_ATTEST_IDS =
ErrorCode.CANNOT_ATTEST_IDS; // -66;
+ public static final int KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE =
+ ErrorCode.ROLLBACK_RESISTANCE_UNAVAILABLE; // -67;
public static final int KM_ERROR_HARDWARE_TYPE_UNAVAILABLE =
ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68;
public static final int KM_ERROR_DEVICE_LOCKED =
ErrorCode.DEVICE_LOCKED; // -72;
+ public static final int KM_ERROR_STORAGE_KEY_UNSUPPORTED =
+ ErrorCode.STORAGE_KEY_UNSUPPORTED; // -77,
+ public static final int KM_ERROR_INCOMPATIBLE_MGF_DIGEST =
+ ErrorCode.INCOMPATIBLE_MGF_DIGEST; // -78,
+ public static final int KM_ERROR_UNSUPPORTED_MGF_DIGEST =
+ ErrorCode.UNSUPPORTED_MGF_DIGEST; // -79,
public static final int KM_ERROR_MISSING_NOT_BEFORE =
ErrorCode.MISSING_NOT_BEFORE; // -80;
public static final int KM_ERROR_MISSING_NOT_AFTER =
ErrorCode.MISSING_NOT_AFTER; // -80;
+ public static final int KM_ERROR_HARDWARE_NOT_YET_AVAILABLE =
+ ErrorCode.HARDWARE_NOT_YET_AVAILABLE; // -85
public static final int KM_ERROR_UNIMPLEMENTED =
ErrorCode.UNIMPLEMENTED; // -100;
public static final int KM_ERROR_VERSION_MISMATCH =
diff --git a/core/java/android/service/cloudsearch/OWNERS b/core/java/android/service/cloudsearch/OWNERS
new file mode 100644
index 000000000000..aa4da3b4bee0
--- /dev/null
+++ b/core/java/android/service/cloudsearch/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 758286
+
+huiwu@google.com
+srazdan@google.com
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 3ab6907557da..4ae3d7de26c2 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -333,7 +333,7 @@ public class DreamService extends Service implements Window.Callback {
public boolean dispatchTouchEvent(MotionEvent event) {
// TODO: create more flexible version of mInteractive that allows clicks
// but finish()es on any other kind of activity
- if (!mInteractive) {
+ if (!mInteractive && event.getActionMasked() == MotionEvent.ACTION_UP) {
if (mDebug) Slog.v(TAG, "Waking up on touchEvent");
wakeUp();
return true;
diff --git a/core/java/android/service/games/CreateGameSessionRequest.aidl b/core/java/android/service/games/CreateGameSessionRequest.aidl
new file mode 100644
index 000000000000..e09cc8e55e3c
--- /dev/null
+++ b/core/java/android/service/games/CreateGameSessionRequest.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+
+/**
+ * @hide
+ */
+parcelable CreateGameSessionRequest; \ No newline at end of file
diff --git a/core/java/android/service/games/CreateGameSessionRequest.java b/core/java/android/service/games/CreateGameSessionRequest.java
new file mode 100644
index 000000000000..2129cb165b93
--- /dev/null
+++ b/core/java/android/service/games/CreateGameSessionRequest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Request object providing the context in order to create a new {@link GameSession}.
+ *
+ * This is provided to the Game Service provider via
+ * {@link GameSessionService#onNewSession(CreateGameSessionRequest)}. It includes game
+ * (see {@link #getGamePackageName()}) that the session is associated with and a task
+ * (see {@link #getTaskId()}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CreateGameSessionRequest implements Parcelable {
+
+ @NonNull
+ public static final Parcelable.Creator<CreateGameSessionRequest> CREATOR =
+ new Parcelable.Creator<CreateGameSessionRequest>() {
+ @Override
+ public CreateGameSessionRequest createFromParcel(Parcel source) {
+ return new CreateGameSessionRequest(
+ source.readInt(),
+ source.readString8());
+ }
+
+ @Override
+ public CreateGameSessionRequest[] newArray(int size) {
+ return new CreateGameSessionRequest[0];
+ }
+ };
+
+ private final int mTaskId;
+ private final String mGamePackageName;
+
+ public CreateGameSessionRequest(int taskId, @NonNull String gamePackageName) {
+ this.mTaskId = taskId;
+ this.mGamePackageName = gamePackageName;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mTaskId);
+ dest.writeString8(mGamePackageName);
+ }
+
+ /**
+ * Unique identifier for the task.
+ */
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ /**
+ * The package name of the game associated with the session.
+ */
+ @NonNull
+ public String getGamePackageName() {
+ return mGamePackageName;
+ }
+
+ @Override
+ public String toString() {
+ return "GameSessionRequest{"
+ + "mTaskId="
+ + mTaskId
+ + ", mGamePackageName='"
+ + mGamePackageName
+ + "\'}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof CreateGameSessionRequest)) {
+ return false;
+ }
+
+ CreateGameSessionRequest that = (CreateGameSessionRequest) o;
+ return mTaskId == that.mTaskId
+ && Objects.equals(mGamePackageName, that.mGamePackageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTaskId, mGamePackageName);
+ }
+}
diff --git a/core/java/android/service/games/GameService.java b/core/java/android/service/games/GameService.java
index 4b440ddf5405..b79c0553460f 100644
--- a/core/java/android/service/games/GameService.java
+++ b/core/java/android/service/games/GameService.java
@@ -46,7 +46,7 @@ import java.util.Objects;
*/
@SystemApi
public class GameService extends Service {
- static final String TAG = "GameService";
+ private static final String TAG = "GameService";
/**
* The {@link Intent} that must be declared as handled by the service.
@@ -55,8 +55,16 @@ public class GameService extends Service {
* that other applications can not abuse it.
*/
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
- public static final String SERVICE_INTERFACE =
- "android.service.games.GameService";
+ public static final String ACTION_GAME_SERVICE =
+ "android.service.games.action.GAME_SERVICE";
+
+ /**
+ * Name under which a GameService component publishes information about itself.
+ * This meta-data should reference an XML resource containing a
+ * <code>&lt;{@link
+ * android.R.styleable#GameService game-session-service}&gt;</code> tag.
+ */
+ public static final String SERVICE_META_DATA = "android.game_service";
private IGameManagerService mGameManagerService;
private final IGameService mInterface = new IGameService.Stub() {
@@ -72,6 +80,7 @@ public class GameService extends Service {
GameService::onDisconnected, GameService.this));
}
};
+
private final IBinder.DeathRecipient mGameManagerServiceDeathRecipient = () -> {
Log.w(TAG, "System service binder died. Shutting down");
@@ -82,9 +91,10 @@ public class GameService extends Service {
@Override
@Nullable
public IBinder onBind(@Nullable Intent intent) {
- if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ if (ACTION_GAME_SERVICE.equals(intent.getAction())) {
return mInterface.asBinder();
}
+
return null;
}
diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java
new file mode 100644
index 000000000000..0ff08c08932b
--- /dev/null
+++ b/core/java/android/service/games/GameSession.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import android.annotation.SystemApi;
+import android.os.Handler;
+
+import com.android.internal.util.function.pooled.PooledLambda;
+
+/**
+ * An active game session, providing a facility for the implementation to interact with the game.
+ *
+ * A Game Service provider should extend the {@link GameSession} to provide their own implementation
+ * which is then returned when a game session is created via
+ * {@link GameSessionService#onNewSession(CreateGameSessionRequest)}.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class GameSession {
+
+ final IGameSession mInterface = new IGameSession.Stub() {
+ @Override
+ public void destroy() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ GameSession::doDestroy, GameSession.this));
+ }
+ };
+
+ void doCreate() {
+ onCreate();
+ }
+
+ void doDestroy() {
+ onDestroy();
+ }
+
+ /**
+ * Initializer called when the game session is starting.
+ *
+ * This should be used perform any setup required now that the game session is created.
+ */
+ public void onCreate() {}
+
+ /**
+ * Finalizer called when the game session is ending.
+ *
+ * This should be used to perform any cleanup before the game session is destroyed.
+ */
+ public void onDestroy() {}
+}
diff --git a/core/java/android/service/games/GameSessionService.java b/core/java/android/service/games/GameSessionService.java
new file mode 100644
index 000000000000..a2c88709b62d
--- /dev/null
+++ b/core/java/android/service/games/GameSessionService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.util.Objects;
+
+/**
+ * Service that hosts active game sessions.
+ *
+ * This service should be in a separate process from the {@link GameService}. This
+ * allows it to perform the heavyweight operations associated with rendering a game
+ * session overlay while games are running and release these resources (by allowing
+ * the process to be killed) when games are not running.
+ *
+ * Game Service providers must extend {@link GameSessionService} and declare the service in their
+ * Manifest. The service must require the {@link android.Manifest.permission#BIND_GAME_SERVICE} so
+ * that other application can not abuse it. This service is used to create instances of
+ * {@link GameSession} via {@link #onNewSession(CreateGameSessionRequest)} and will remain bound to
+ * so long as at least one {@link GameSession} is running.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class GameSessionService extends Service {
+ private static final String TAG = "GameSessionService";
+
+ /**
+ * The {@link Intent} action used when binding to the service.
+ * To be supported, the service must require the
+ * {@link android.Manifest.permission#BIND_GAME_SERVICE} permission so
+ * that other applications can not abuse it.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String ACTION_GAME_SESSION_SERVICE =
+ "android.service.games.action.GAME_SESSION_SERVICE";
+
+ private final IGameSessionService mInterface = new IGameSessionService.Stub() {
+ @Override
+ public void create(CreateGameSessionRequest createGameSessionRequest,
+ AndroidFuture gameSessionFuture) {
+ Handler.getMain().post(PooledLambda.obtainRunnable(
+ GameSessionService::doCreate, GameSessionService.this,
+ createGameSessionRequest,
+ gameSessionFuture));
+ }
+ };
+
+ @Override
+ @Nullable
+ public IBinder onBind(@Nullable Intent intent) {
+ if (intent == null) {
+ return null;
+ }
+
+ if (!ACTION_GAME_SESSION_SERVICE.equals(intent.getAction())) {
+ return null;
+ }
+
+ return mInterface.asBinder();
+ }
+
+ private void doCreate(CreateGameSessionRequest createGameSessionRequest,
+ AndroidFuture<IBinder> gameSessionFuture) {
+ GameSession gameSession = onNewSession(createGameSessionRequest);
+ Objects.requireNonNull(gameSession);
+
+ gameSessionFuture.complete(gameSession.mInterface.asBinder());
+
+ gameSession.doCreate();
+ }
+
+ /**
+ * Request to create a new {@link GameSession}.
+ */
+ @NonNull
+ public abstract GameSession onNewSession(
+ @NonNull CreateGameSessionRequest createGameSessionRequest);
+}
diff --git a/core/java/android/service/games/IGameSession.aidl b/core/java/android/service/games/IGameSession.aidl
new file mode 100644
index 000000000000..b2e9f1d21f6e
--- /dev/null
+++ b/core/java/android/service/games/IGameSession.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+/**
+ * @hide
+ */
+oneway interface IGameSession {
+ void destroy();
+}
diff --git a/core/java/android/service/games/IGameSessionService.aidl b/core/java/android/service/games/IGameSessionService.aidl
new file mode 100644
index 000000000000..2a53ea7f8e4a
--- /dev/null
+++ b/core/java/android/service/games/IGameSessionService.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import android.service.games.IGameSession;
+import android.service.games.CreateGameSessionRequest;
+
+import com.android.internal.infra.AndroidFuture;
+
+
+/**
+ * @hide
+ */
+oneway interface IGameSessionService {
+ void create(
+ in CreateGameSessionRequest createGameSessionRequest,
+ in AndroidFuture /* T=IBinder for IGameSession */ gameSessionFuture);
+}
diff --git a/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl b/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl
new file mode 100644
index 000000000000..2bd99acbf24a
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.selectiontoolbar;
+
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+
+/**
+ * The service to render the selection toolbar menus.
+ *
+ * @hide
+ */
+oneway interface ISelectionToolbarRenderService {
+ void onShow(in ShowInfo showInfo, in ISelectionToolbarCallback callback);
+ void onHide(long widgetToken);
+ void onDismiss(long widgetToken);
+}
diff --git a/core/java/android/service/selectiontoolbar/OWNERS b/core/java/android/service/selectiontoolbar/OWNERS
new file mode 100644
index 000000000000..5500b92868dd
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 709498
+
+augale@google.com
+joannechung@google.com
+licha@google.com
+lpeter@google.com
+svetoslavganov@google.com
+toki@google.com
+tonymak@google.com
+tymtsai@google.com \ No newline at end of file
diff --git a/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java
new file mode 100644
index 000000000000..6468183880f4
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.selectiontoolbar;
+
+import android.view.selectiontoolbar.ToolbarMenuItem;
+import android.view.selectiontoolbar.WidgetInfo;
+
+/**
+ * The callback that the render service uses to communicate with the host of the selection toolbar
+ * container.
+ *
+ * @hide
+ */
+public interface SelectionToolbarRenderCallback {
+ /**
+ * The selection toolbar is shown.
+ */
+ void onShown(WidgetInfo widgetInfo);
+ /**
+ * The selection toolbar is hidden.
+ */
+ void onHidden(long widgetToken);
+ /**
+ * The selection toolbar is dismissed.
+ */
+ void onDismissed(long widgetToken);
+ /**
+ * The selection toolbar has changed.
+ */
+ void onWidgetUpdated(WidgetInfo info);
+ /**
+ * The menu item on the selection toolbar has been clicked.
+ */
+ void onMenuItemClicked(ToolbarMenuItem item);
+ /**
+ * The error occurred when operating on the selection toolbar.
+ */
+ void onError(int errorCode);
+}
diff --git a/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java
new file mode 100644
index 000000000000..6f66c9f1ed3a
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.selectiontoolbar;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+import android.view.selectiontoolbar.ToolbarMenuItem;
+import android.view.selectiontoolbar.WidgetInfo;
+
+/**
+ * Service for rendering selection toolbar.
+ *
+ * @hide
+ */
+public abstract class SelectionToolbarRenderService extends Service {
+
+ private static final String TAG = "SelectionToolbarRenderService";
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ *
+ * <p>To be supported, the service must also require the
+ * {@link android.Manifest.permission#BIND_SELECTION_TOOLBAR_RENDER_SERVICE} permission so
+ * that other applications can not abuse it.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.selectiontoolbar.SelectionToolbarRenderService";
+
+ private Handler mHandler;
+
+ /**
+ * Binder to receive calls from system server.
+ */
+ private final ISelectionToolbarRenderService mInterface =
+ new ISelectionToolbarRenderService.Stub() {
+
+ @Override
+ public void onShow(ShowInfo showInfo, ISelectionToolbarCallback callback) {
+ mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onShow,
+ SelectionToolbarRenderService.this, showInfo,
+ new RemoteCallbackWrapper(callback)));
+ }
+
+ @Override
+ public void onHide(long widgetToken) {
+ mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onHide,
+ SelectionToolbarRenderService.this, widgetToken));
+ }
+
+ @Override
+ public void onDismiss(long widgetToken) {
+ mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onDismiss,
+ SelectionToolbarRenderService.this, widgetToken));
+ }
+ };
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ @Override
+ @Nullable
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+ return null;
+ }
+
+
+ /**
+ * Called when showing the selection toolbar.
+ */
+ public abstract void onShow(ShowInfo showInfo, RemoteCallbackWrapper callbackWrapper);
+
+ /**
+ * Called when hiding the selection toolbar.
+ */
+ public abstract void onHide(long widgetToken);
+
+
+ /**
+ * Called when dismissing the selection toolbar.
+ */
+ public abstract void onDismiss(long widgetToken);
+
+ /**
+ * Add avadoc.
+ */
+ public static final class RemoteCallbackWrapper implements SelectionToolbarRenderCallback {
+
+ private final ISelectionToolbarCallback mRemoteCallback;
+
+ RemoteCallbackWrapper(ISelectionToolbarCallback remoteCallback) {
+ mRemoteCallback = remoteCallback;
+ }
+
+ @Override
+ public void onShown(WidgetInfo widgetInfo) {
+ try {
+ mRemoteCallback.onShown(widgetInfo);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onHidden(long widgetToken) {
+ try {
+ mRemoteCallback.onHidden(widgetToken);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onDismissed(long widgetToken) {
+ try {
+ mRemoteCallback.onDismissed(widgetToken);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onWidgetUpdated(WidgetInfo widgetInfo) {
+ try {
+ mRemoteCallback.onWidgetUpdated(widgetInfo);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onMenuItemClicked(ToolbarMenuItem item) {
+ try {
+ mRemoteCallback.onMenuItemClicked(item);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onError(int errorCode) {
+ try {
+ mRemoteCallback.onError(errorCode);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+ }
+}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 5b9d69c2f9ff..e5c9adba46a9 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -743,6 +743,7 @@ public class PhoneStateListener {
* @see TelephonyManager#DATA_CONNECTING
* @see TelephonyManager#DATA_CONNECTED
* @see TelephonyManager#DATA_SUSPENDED
+ * @see TelephonyManager#DATA_HANDOVER_IN_PROGRESS
* @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead.
*/
@Deprecated
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 3028a6d8f97a..baa9e6b184e9 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -792,6 +792,7 @@ public class TelephonyCallback {
* @see TelephonyManager#DATA_CONNECTING
* @see TelephonyManager#DATA_CONNECTED
* @see TelephonyManager#DATA_SUSPENDED
+ * @see TelephonyManager#DATA_HANDOVER_IN_PROGRESS
*/
void onDataConnectionStateChanged(@TelephonyManager.DataState int state,
@Annotation.NetworkType int networkType);
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 859fd804dea5..9eaaa91532d0 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -36,18 +36,24 @@ import android.telephony.Annotation.PreciseDisconnectCauses;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.SrvccState;
+import android.telephony.TelephonyManager.CarrierPrivilegesListener;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.listeners.ListenerExecutor;
+import com.android.internal.telephony.ICarrierPrivilegesListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.ITelephonyRegistry;
+import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.WeakHashMap;
import java.util.concurrent.Executor;
/**
@@ -285,6 +291,8 @@ public class TelephonyRegistryManager {
* UI. There is no timeout associated with showing this UX, so a carrier app must be sure to
* call with active set to false sometime after calling with it set to {@code true}.
* <p>
+ * This will apply to all subscriptions the carrier app has carrier privileges on.
+ * <p>
* Requires Permission: calling app has carrier privileges.
*
* @param active Whether the carrier network change is or shortly will be
@@ -300,6 +308,31 @@ public class TelephonyRegistryManager {
}
/**
+ * Informs the system of an intentional upcoming carrier network change by a carrier app on the
+ * given {@code subscriptionId}. This call only used to allow the system to provide alternative
+ * UI while telephony is performing an action that may result in intentional, temporary network
+ * lack of connectivity.
+ * <p>
+ * Based on the active parameter passed in, this method will either show or hide the
+ * alternative UI. There is no timeout associated with showing this UX, so a carrier app must be
+ * sure to call with active set to false sometime after calling with it set to {@code true}.
+ * <p>
+ * Requires Permission: calling app has carrier privileges.
+ *
+ * @param subscriptionId the subscription of the carrier network.
+ * @param active whether the carrier network change is or shortly will be active. Set this value
+ * to true to begin showing alternative UI and false to stop.
+ * @see TelephonyManager#hasCarrierPrivileges
+ */
+ public void notifyCarrierNetworkChange(int subscriptionId, boolean active) {
+ try {
+ sRegistry.notifyCarrierNetworkChangeWithSubId(subscriptionId, active);
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
* Notify call state changed on certain subscription.
*
* @param slotIndex for which call state changed. Can be derived from subId except when subId is
@@ -1187,4 +1220,117 @@ public class TelephonyRegistryManager {
listenFromCallback(false, false, subId,
pkgName, attributionTag, callback, new int[0], notifyNow);
}
+
+ private static class CarrierPrivilegesListenerWrapper extends ICarrierPrivilegesListener.Stub
+ implements ListenerExecutor {
+ private final WeakReference<CarrierPrivilegesListener> mListener;
+ private final Executor mExecutor;
+
+ CarrierPrivilegesListenerWrapper(CarrierPrivilegesListener listener, Executor executor) {
+ mListener = new WeakReference<>(listener);
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onCarrierPrivilegesChanged(
+ List<String> privilegedPackageNames, int[] privilegedUids) {
+ Binder.withCleanCallingIdentity(
+ () ->
+ executeSafely(
+ mExecutor,
+ mListener::get,
+ cpl ->
+ cpl.onCarrierPrivilegesChanged(
+ privilegedPackageNames, privilegedUids)));
+ }
+ }
+
+ @GuardedBy("sCarrierPrivilegeListeners")
+ private static final WeakHashMap<
+ CarrierPrivilegesListener, WeakReference<CarrierPrivilegesListenerWrapper>>
+ sCarrierPrivilegeListeners = new WeakHashMap<>();
+
+ /**
+ * Registers a {@link CarrierPrivilegesListener} on the given {@code logicalSlotIndex} to
+ * receive callbacks when the set of packages with carrier privileges changes. The callback will
+ * immediately be called with the latest state.
+ *
+ * @param logicalSlotIndex The SIM slot to listen on
+ * @param executor The executor where {@code listener} will be invoked
+ * @param listener The callback to register
+ */
+ public void addCarrierPrivilegesListener(
+ int logicalSlotIndex,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CarrierPrivilegesListener listener) {
+ if (listener == null || executor == null) {
+ throw new IllegalArgumentException("listener and executor must be non-null");
+ }
+ synchronized (sCarrierPrivilegeListeners) {
+ WeakReference<CarrierPrivilegesListenerWrapper> existing =
+ sCarrierPrivilegeListeners.get(listener);
+ if (existing != null && existing.get() != null) {
+ Log.d(TAG, "addCarrierPrivilegesListener: listener already registered");
+ return;
+ }
+ CarrierPrivilegesListenerWrapper wrapper =
+ new CarrierPrivilegesListenerWrapper(listener, executor);
+ sCarrierPrivilegeListeners.put(listener, new WeakReference<>(wrapper));
+ try {
+ sRegistry.addCarrierPrivilegesListener(
+ logicalSlotIndex,
+ wrapper,
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters a {@link CarrierPrivilegesListener}.
+ *
+ * @param listener The callback to unregister
+ */
+ public void removeCarrierPrivilegesListener(@NonNull CarrierPrivilegesListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must be non-null");
+ }
+ synchronized (sCarrierPrivilegeListeners) {
+ WeakReference<CarrierPrivilegesListenerWrapper> ref =
+ sCarrierPrivilegeListeners.remove(listener);
+ if (ref == null) return;
+ CarrierPrivilegesListenerWrapper wrapper = ref.get();
+ if (wrapper == null) return;
+ try {
+ sRegistry.removeCarrierPrivilegesListener(wrapper, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Notify listeners that the set of packages with carrier privileges has changed.
+ *
+ * @param logicalSlotIndex The SIM slot the change occurred on
+ * @param privilegedPackageNames The updated set of packages names with carrier privileges
+ * @param privilegedUids The updated set of UIDs with carrier privileges
+ */
+ public void notifyCarrierPrivilegesChanged(
+ int logicalSlotIndex,
+ @NonNull List<String> privilegedPackageNames,
+ @NonNull int[] privilegedUids) {
+ if (privilegedPackageNames == null || privilegedUids == null) {
+ throw new IllegalArgumentException(
+ "privilegedPackageNames and privilegedUids must be non-null");
+ }
+ try {
+ sRegistry.notifyCarrierPrivilegesChanged(
+ logicalSlotIndex, privilegedPackageNames, privilegedUids);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 8124510718ae..52f1faef0fc3 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -44,8 +44,6 @@ public class FeatureFlagUtils {
public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
"settings_do_not_restore_preserved";
/** @hide */
- public static final String SETTINGS_PROVIDER_MODEL = "settings_provider_model";
- /** @hide */
public static final String SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES
= "settings_use_new_backup_eligibility_rules";
/** @hide */
@@ -80,7 +78,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
DEFAULT_FLAGS.put("settings_contextual_home", "false");
- DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "true");
DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
@@ -92,7 +89,6 @@ public class FeatureFlagUtils {
static {
PERSISTENT_FLAGS = new HashSet<>();
PERSISTENT_FLAGS.add(SETTINGS_APP_LANGUAGE_SELECTION);
- PERSISTENT_FLAGS.add(SETTINGS_PROVIDER_MODEL);
PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
}
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 6da38c2c2acb..5cbbbef2cf88 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -75,7 +75,7 @@ public final class MemoryIntArray implements Parcelable, Closeable {
final String name = UUID.randomUUID().toString();
mFd = nativeCreate(name, size);
mMemoryAddr = nativeOpen(mFd, mIsOwner);
- mCloseGuard.open("close");
+ mCloseGuard.open("MemoryIntArray.close");
}
private MemoryIntArray(Parcel parcel) throws IOException {
@@ -86,7 +86,7 @@ public final class MemoryIntArray implements Parcelable, Closeable {
}
mFd = pfd.detachFd();
mMemoryAddr = nativeOpen(mFd, mIsOwner);
- mCloseGuard.open("close");
+ mCloseGuard.open("MemoryIntArray.close");
}
/**
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index c9abec989cd1..a24c1f95b0c0 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -79,7 +79,7 @@ public abstract class InputEventReceiver {
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
- mCloseGuard.open("dispose");
+ mCloseGuard.open("InputEventReceiver.dispose");
}
@Override
diff --git a/core/java/android/view/InputEventSender.java b/core/java/android/view/InputEventSender.java
index d14421897860..9035f3f7a0d4 100644
--- a/core/java/android/view/InputEventSender.java
+++ b/core/java/android/view/InputEventSender.java
@@ -67,7 +67,7 @@ public abstract class InputEventSender {
mSenderPtr = nativeInit(new WeakReference<InputEventSender>(this),
inputChannel, mMessageQueue);
- mCloseGuard.open("dispose");
+ mCloseGuard.open("InputEventSender.dispose");
}
@Override
diff --git a/core/java/android/view/InputQueue.java b/core/java/android/view/InputQueue.java
index 7accb66aa3aa..ff51ebcca08e 100644
--- a/core/java/android/view/InputQueue.java
+++ b/core/java/android/view/InputQueue.java
@@ -52,7 +52,7 @@ public final class InputQueue {
public InputQueue() {
mPtr = nativeInit(new WeakReference<InputQueue>(this), Looper.myQueue());
- mCloseGuard.open("dispose");
+ mCloseGuard.open("InputQueue.dispose");
}
@Override
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index bf9de39124c9..b1582cf9f023 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -180,10 +180,7 @@ public class InsetsSourceConsumer {
// If we have a new leash, make sure visibility is up-to-date, even though we
// didn't want to run an animation above.
- SurfaceControl newLeash = mSourceControl.getLeash();
- if (oldLeash == null || newLeash == null || !oldLeash.isSameSurface(newLeash)) {
- applyHiddenToControl();
- }
+ applyRequestedVisibilityToControl();
// Remove the surface that owned by last control when it lost.
if (!requestedVisible && !mIsAnimationPending && lastControl == null) {
@@ -388,18 +385,20 @@ public class InsetsSourceConsumer {
}
}
- private void applyHiddenToControl() {
+ private void applyRequestedVisibilityToControl() {
if (mSourceControl == null || mSourceControl.getLeash() == null) {
return;
}
final Transaction t = mTransactionSupplier.get();
- if (DEBUG) Log.d(TAG, "applyHiddenToControl: " + mRequestedVisible);
+ if (DEBUG) Log.d(TAG, "applyRequestedVisibilityToControl: " + mRequestedVisible);
if (mRequestedVisible) {
t.show(mSourceControl.getLeash());
} else {
t.hide(mSourceControl.getLeash());
}
+ // Ensure the alpha value is aligned with the actual requested visibility.
+ t.setAlpha(mSourceControl.getLeash(), mRequestedVisible ? 1 : 0);
t.apply();
onPerceptible(mRequestedVisible);
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index adb8b86493d5..8db62f6553eb 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1872,7 +1872,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
float x, float y, float pressure, float size, int metaState,
float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
return obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
- xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_UNKNOWN,
+ xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_CLASS_POINTER,
DEFAULT_DISPLAY);
}
diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java
index 278b2fcc3678..cba0e970d389 100644
--- a/core/java/android/view/ScrollCaptureConnection.java
+++ b/core/java/android/view/ScrollCaptureConnection.java
@@ -86,7 +86,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub imple
@Override
public ICancellationSignal startCapture(@NonNull Surface surface,
@NonNull IScrollCaptureCallbacks remote) throws RemoteException {
- mCloseGuard.open("close");
+ mCloseGuard.open("ScrollCaptureConnection.close");
if (!surface.isValid()) {
throw new RemoteException(new IllegalArgumentException("surface must be valid"));
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 904aa73f6ac4..e5ec260907df 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -755,7 +755,7 @@ public class Surface implements Parcelable {
private void setNativeObjectLocked(long ptr) {
if (mNativeObject != ptr) {
if (mNativeObject == 0 && ptr != 0) {
- mCloseGuard.open("release");
+ mCloseGuard.open("Surface.release");
} else if (mNativeObject != 0 && ptr == 0) {
mCloseGuard.close();
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 70505fcc09b5..c37120220f1a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -93,6 +93,7 @@ import android.annotation.Nullable;
import android.annotation.UiContext;
import android.app.ActivityManager;
import android.app.ActivityThread;
+import android.app.ICompatCameraControlCallback;
import android.app.ResourcesManager;
import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
@@ -317,7 +318,7 @@ public final class ViewRootImpl implements ViewParent,
private static final ArrayList<ConfigChangedCallback> sConfigCallbacks = new ArrayList<>();
/**
- * Callback for notifying activities about override configuration changes.
+ * Callback for notifying activities.
*/
public interface ActivityConfigCallback {
@@ -327,11 +328,23 @@ public final class ViewRootImpl implements ViewParent,
* @param newDisplayId New display id, {@link Display#INVALID_DISPLAY} if not changed.
*/
void onConfigurationChanged(Configuration overrideConfig, int newDisplayId);
+
+ /**
+ * Notify the corresponding activity about the request to show or hide a camera compat
+ * control for stretched issues in the viewfinder.
+ *
+ * @param showControl Whether the control should be shown or hidden.
+ * @param transformationApplied Whether the treatment is already applied.
+ * @param callback The callback executed when the user clicks on a control.
+ */
+ void requestCompatCameraControl(boolean showControl, boolean transformationApplied,
+ ICompatCameraControlCallback callback);
}
/**
- * Callback used to notify corresponding activity about override configuration change and make
- * sure that all resources are set correctly before updating the ViewRootImpl's internal state.
+ * Callback used to notify corresponding activity about camera compat control changes, override
+ * configuration change and make sure that all resources are set correctly before updating the
+ * ViewRootImpl's internal state.
*/
private ActivityConfigCallback mActivityConfigCallback;
@@ -865,7 +878,10 @@ public final class ViewRootImpl implements ViewParent,
}
}
- /** Add activity config callback to be notified about override config changes. */
+ /**
+ * Add activity config callback to be notified about override config changes and camera
+ * compat control state updates.
+ */
public void setActivityConfigCallback(ActivityConfigCallback callback) {
mActivityConfigCallback = callback;
}
@@ -10498,6 +10514,20 @@ public final class ViewRootImpl implements ViewParent,
}
/**
+ * Shows or hides a Camera app compat toggle for stretched issues with the requested state
+ * for the corresponding activity.
+ *
+ * @param showControl Whether the control should be shown or hidden.
+ * @param transformationApplied Whether the treatment is already applied.
+ * @param callback The callback executed when the user clicks on a control.
+ */
+ public void requestCompatCameraControl(boolean showControl, boolean transformationApplied,
+ ICompatCameraControlCallback callback) {
+ mActivityConfigCallback.requestCompatCameraControl(
+ showControl, transformationApplied, callback);
+ }
+
+ /**
* Redirect the next draw of this ViewRoot (from the UI thread perspective)
* to the passed in consumer. This can be used to create P2P synchronization
* between ViewRoot's however it comes with many caveats.
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 96198c64fa56..7e6e6fd23654 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -141,6 +141,12 @@ public final class InputMethodInfo implements Parcelable {
private final int mHandledConfigChanges;
/**
+ * The flag whether this IME supports Handwriting using stylus input.
+ */
+ private final boolean mSupportsStylusHandwriting;
+
+
+ /**
* @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
* @return a unique ID to be returned by {@link #getId()}. We have used
* {@link ComponentName#flattenToShortString()} for this purpose (and it is already
@@ -234,6 +240,8 @@ public final class InputMethodInfo implements Parcelable {
com.android.internal.R.styleable.InputMethod_showInInputMethodPicker, true);
mHandledConfigChanges = sa.getInt(
com.android.internal.R.styleable.InputMethod_configChanges, 0);
+ mSupportsStylusHandwriting = sa.getBoolean(
+ com.android.internal.R.styleable.InputMethod_supportsStylusHandwriting, false);
sa.recycle();
final int depth = parser.getDepth();
@@ -323,6 +331,7 @@ public final class InputMethodInfo implements Parcelable {
mService = ResolveInfo.CREATOR.createFromParcel(source);
mSubtypes = new InputMethodSubtypeArray(source);
mHandledConfigChanges = source.readInt();
+ mSupportsStylusHandwriting = source.readBoolean();
mForceDefault = false;
}
@@ -335,7 +344,7 @@ public final class InputMethodInfo implements Parcelable {
settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
- 0 /* handledConfigChanges */);
+ 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */);
}
/**
@@ -349,7 +358,8 @@ public final class InputMethodInfo implements Parcelable {
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
- false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges);
+ false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges,
+ false /* supportsStylusHandwriting */);
}
/**
@@ -361,7 +371,8 @@ public final class InputMethodInfo implements Parcelable {
boolean forceDefault) {
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
- false /* isVrOnly */, 0 /* handledconfigChanges */);
+ false /* isVrOnly */, 0 /* handledconfigChanges */,
+ false /* supportsStylusHandwriting */);
}
/**
@@ -373,7 +384,7 @@ public final class InputMethodInfo implements Parcelable {
boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly,
- 0 /* handledConfigChanges */);
+ 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */);
}
/**
@@ -383,7 +394,7 @@ public final class InputMethodInfo implements Parcelable {
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
- boolean isVrOnly, int handledConfigChanges) {
+ boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting) {
final ServiceInfo si = ri.serviceInfo;
mService = ri;
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -398,6 +409,7 @@ public final class InputMethodInfo implements Parcelable {
mShowInInputMethodPicker = true;
mIsVrOnly = isVrOnly;
mHandledConfigChanges = handledConfigChanges;
+ mSupportsStylusHandwriting = supportsStylusHandwriting;
}
private static ResolveInfo buildFakeResolveInfo(String packageName, String className,
@@ -556,6 +568,14 @@ public final class InputMethodInfo implements Parcelable {
return mHandledConfigChanges;
}
+ /**
+ * Returns if IME supports handwriting using stylus input.
+ * @attr ref android.R.styleable#InputMethod_supportsStylusHandwriting
+ */
+ public boolean supportsStylusHandwriting() {
+ return mSupportsStylusHandwriting;
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
+ " mSettingsActivityName=" + mSettingsActivityName
@@ -563,7 +583,8 @@ public final class InputMethodInfo implements Parcelable {
+ " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
+ " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled
+ " mSuppressesSpellChecker=" + mSuppressesSpellChecker
- + " mShowInInputMethodPicker=" + mShowInInputMethodPicker);
+ + " mShowInInputMethodPicker=" + mShowInInputMethodPicker
+ + " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting);
pw.println(prefix + "mIsDefaultResId=0x"
+ Integer.toHexString(mIsDefaultResId));
pw.println(prefix + "Service:");
@@ -667,6 +688,7 @@ public final class InputMethodInfo implements Parcelable {
mService.writeToParcel(dest, flags);
mSubtypes.writeToParcel(dest);
dest.writeInt(mHandledConfigChanges);
+ dest.writeBoolean(mSupportsStylusHandwriting);
}
/**
diff --git a/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl b/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl
new file mode 100644
index 000000000000..48af7b96b195
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+import android.view.selectiontoolbar.ToolbarMenuItem;
+import android.view.selectiontoolbar.WidgetInfo;
+
+/**
+ * Binder interface to notify the selection toolbar events from one process to the other.
+ * @hide
+ */
+oneway interface ISelectionToolbarCallback {
+ void onShown(in WidgetInfo info);
+ void onHidden(long widgetToken);
+ void onDismissed(long widgetToken);
+ void onWidgetUpdated(in WidgetInfo info);
+ void onMenuItemClicked(in ToolbarMenuItem item);
+ void onError(int errorCode);
+}
diff --git a/core/java/android/view/selectiontoolbar/ISelectionToolbarManager.aidl b/core/java/android/view/selectiontoolbar/ISelectionToolbarManager.aidl
new file mode 100644
index 000000000000..4a647ada1d6c
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ISelectionToolbarManager.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+
+/**
+ * Mediator between apps and selection toolbar service implementation.
+ *
+ * @hide
+ */
+oneway interface ISelectionToolbarManager {
+ void showToolbar(in ShowInfo showInfo, in ISelectionToolbarCallback callback, int userId);
+ void hideToolbar(long widgetToken, int userId);
+ void dismissToolbar(long widgetToken, int userId);
+} \ No newline at end of file
diff --git a/core/java/android/view/selectiontoolbar/OWNERS b/core/java/android/view/selectiontoolbar/OWNERS
new file mode 100644
index 000000000000..5500b92868dd
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 709498
+
+augale@google.com
+joannechung@google.com
+licha@google.com
+lpeter@google.com
+svetoslavganov@google.com
+toki@google.com
+tonymak@google.com
+tymtsai@google.com \ No newline at end of file
diff --git a/core/java/android/view/selectiontoolbar/SelectionContext.aidl b/core/java/android/view/selectiontoolbar/SelectionContext.aidl
new file mode 100644
index 000000000000..52068312d4a1
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/SelectionContext.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+/**
+ * @hide
+ */
+parcelable SelectionContext;
diff --git a/core/java/android/view/selectiontoolbar/SelectionContext.java b/core/java/android/view/selectiontoolbar/SelectionContext.java
new file mode 100644
index 000000000000..21b8d8f11d25
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/SelectionContext.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * The class holds information for a selection.
+ *
+ * @hide
+ */
+@DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true)
+public final class SelectionContext implements Parcelable {
+
+ /**
+ * The start index of a selection.
+ */
+ private final int mStartIndex;
+
+ /**
+ * The end index of a selection.
+ */
+ private final int mEndIndex;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/SelectionContext.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ SelectionContext(
+ int startIndex,
+ int endIndex) {
+ this.mStartIndex = startIndex;
+ this.mEndIndex = endIndex;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The start index of a selection.
+ */
+ @DataClass.Generated.Member
+ public int getStartIndex() {
+ return mStartIndex;
+ }
+
+ /**
+ * The end index of a selection.
+ */
+ @DataClass.Generated.Member
+ public int getEndIndex() {
+ return mEndIndex;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "SelectionContext { " +
+ "startIndex = " + mStartIndex + ", " +
+ "endIndex = " + mEndIndex +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(SelectionContext other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ SelectionContext that = (SelectionContext) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mStartIndex == that.mStartIndex
+ && mEndIndex == that.mEndIndex;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mStartIndex;
+ _hash = 31 * _hash + mEndIndex;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mStartIndex);
+ dest.writeInt(mEndIndex);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ SelectionContext(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int startIndex = in.readInt();
+ int endIndex = in.readInt();
+
+ this.mStartIndex = startIndex;
+ this.mEndIndex = endIndex;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<SelectionContext> CREATOR
+ = new Parcelable.Creator<SelectionContext>() {
+ @Override
+ public SelectionContext[] newArray(int size) {
+ return new SelectionContext[size];
+ }
+
+ @Override
+ public SelectionContext createFromParcel(@NonNull android.os.Parcel in) {
+ return new SelectionContext(in);
+ }
+ };
+
+ /**
+ * A builder for {@link SelectionContext}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private int mStartIndex;
+ private int mEndIndex;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param startIndex
+ * The start index of a selection.
+ * @param endIndex
+ * The end index of a selection.
+ */
+ public Builder(
+ int startIndex,
+ int endIndex) {
+ mStartIndex = startIndex;
+ mEndIndex = endIndex;
+ }
+
+ /**
+ * The start index of a selection.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setStartIndex(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mStartIndex = value;
+ return this;
+ }
+
+ /**
+ * The end index of a selection.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setEndIndex(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mEndIndex = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull SelectionContext build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ SelectionContext o = new SelectionContext(
+ mStartIndex,
+ mEndIndex);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1639488292248L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/SelectionContext.java",
+ inputSignatures = "private final int mStartIndex\nprivate final int mEndIndex\nclass SelectionContext extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
new file mode 100644
index 000000000000..60688eaa3600
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+
+import java.util.Objects;
+
+/**
+ * The {@link SelectionToolbarManager} class provides ways for apps to control the
+ * selection toolbar.
+ *
+ * @hide
+ */
+@SystemService(Context.SELECTION_TOOLBAR_SERVICE)
+public final class SelectionToolbarManager {
+
+ private static final String TAG = "SelectionToolbar";
+
+ /**
+ * The tag which uses for enabling debug log dump. To enable it, we can use command "adb shell
+ * setprop log.tag.UiTranslation DEBUG".
+ */
+ public static final String LOG_TAG = "SelectionToolbar";
+
+
+ @NonNull
+ private final Context mContext;
+ private final ISelectionToolbarManager mService;
+
+ public SelectionToolbarManager(@NonNull Context context,
+ @NonNull ISelectionToolbarManager service) {
+ mContext = Objects.requireNonNull(context);
+ mService = service;
+ }
+
+ /**
+ * Request to show selection toolbar for a given View.
+ */
+ public void showToolbar(@NonNull ShowInfo showInfo,
+ @NonNull ISelectionToolbarCallback callback) {
+ try {
+ Objects.requireNonNull(showInfo);
+ Objects.requireNonNull(callback);
+ mService.showToolbar(showInfo, callback, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request to hide selection toolbar.
+ */
+ public void hideToolbar(long widgetToken) {
+ try {
+ mService.hideToolbar(widgetToken, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Dismiss to dismiss selection toolbar.
+ */
+ public void dismissToolbar(long widgetToken) {
+ try {
+ mService.dismissToolbar(widgetToken, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/view/selectiontoolbar/ShowInfo.aidl b/core/java/android/view/selectiontoolbar/ShowInfo.aidl
new file mode 100644
index 000000000000..dce9c15dce3e
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ShowInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+/**
+ * @hide
+ */
+parcelable ShowInfo;
diff --git a/core/java/android/view/selectiontoolbar/ShowInfo.java b/core/java/android/view/selectiontoolbar/ShowInfo.java
new file mode 100644
index 000000000000..bbbd5c0df0b4
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ShowInfo.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+
+/**
+ * The class holds menu information for render service to render the selection toolbar.
+ *
+ * @hide
+ */
+@DataClass(genToString = true, genEqualsHashCode = true)
+public final class ShowInfo implements Parcelable {
+ /**
+ * The token that is used to identify the selection toolbar. This is initially set to 0
+ * until a selection toolbar has been created for the showToolbar request.
+ */
+ private final long mWidgetToken;
+
+ // TODO: add members when the code really uses it
+
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/ShowInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new ShowInfo.
+ *
+ * @param widgetToken
+ * The token that is used to identify the selection toolbar.
+ */
+ @DataClass.Generated.Member
+ public ShowInfo(
+ long widgetToken) {
+ this.mWidgetToken = widgetToken;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The token that is used to identify the selection toolbar.
+ */
+ @DataClass.Generated.Member
+ public long getWidgetToken() {
+ return mWidgetToken;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "ShowInfo { " +
+ "widgetToken = " + mWidgetToken +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(ShowInfo other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ ShowInfo that = (ShowInfo) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mWidgetToken == that.mWidgetToken;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Long.hashCode(mWidgetToken);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeLong(mWidgetToken);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ ShowInfo(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ long widgetToken = in.readLong();
+
+ this.mWidgetToken = widgetToken;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ShowInfo> CREATOR
+ = new Parcelable.Creator<ShowInfo>() {
+ @Override
+ public ShowInfo[] newArray(int size) {
+ return new ShowInfo[size];
+ }
+
+ @Override
+ public ShowInfo createFromParcel(@NonNull android.os.Parcel in) {
+ return new ShowInfo(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1639488262761L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/ShowInfo.java",
+ inputSignatures = "private final long mWidgetToken\nclass ShowInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/selectiontoolbar/ToolbarMenuItem.aidl b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.aidl
new file mode 100644
index 000000000000..711a85a7771e
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+/**
+ * @hide
+ */
+parcelable ToolbarMenuItem;
diff --git a/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java
new file mode 100644
index 000000000000..5af232c7a34d
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * The menu item that is used to show the selection toolbar.
+ *
+ * @hide
+ */
+@DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true)
+public final class ToolbarMenuItem implements Parcelable {
+
+ /**
+ * The id of the menu item.
+ */
+ private final int mItemId;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ ToolbarMenuItem(
+ int itemId) {
+ this.mItemId = itemId;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The id of the menu item.
+ */
+ @DataClass.Generated.Member
+ public int getItemId() {
+ return mItemId;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "ToolbarMenuItem { " +
+ "itemId = " + mItemId +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(ToolbarMenuItem other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ ToolbarMenuItem that = (ToolbarMenuItem) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mItemId == that.mItemId;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mItemId;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mItemId);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ ToolbarMenuItem(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int itemId = in.readInt();
+
+ this.mItemId = itemId;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ToolbarMenuItem> CREATOR
+ = new Parcelable.Creator<ToolbarMenuItem>() {
+ @Override
+ public ToolbarMenuItem[] newArray(int size) {
+ return new ToolbarMenuItem[size];
+ }
+
+ @Override
+ public ToolbarMenuItem createFromParcel(@NonNull android.os.Parcel in) {
+ return new ToolbarMenuItem(in);
+ }
+ };
+
+ /**
+ * A builder for {@link ToolbarMenuItem}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private int mItemId;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param itemId
+ * The id of the menu item.
+ */
+ public Builder(
+ int itemId) {
+ mItemId = itemId;
+ }
+
+ /**
+ * The id of the menu item.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setItemId(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mItemId = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull ToolbarMenuItem build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2; // Mark builder used
+
+ ToolbarMenuItem o = new ToolbarMenuItem(
+ mItemId);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x2) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1639488328542L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java",
+ inputSignatures = "private final int mItemId\nclass ToolbarMenuItem extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/selectiontoolbar/WidgetInfo.aidl b/core/java/android/view/selectiontoolbar/WidgetInfo.aidl
new file mode 100644
index 000000000000..1057c516e233
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/WidgetInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+/**
+ * @hide
+ */
+parcelable WidgetInfo;
diff --git a/core/java/android/view/selectiontoolbar/WidgetInfo.java b/core/java/android/view/selectiontoolbar/WidgetInfo.java
new file mode 100644
index 000000000000..961d8ac8e5e0
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/WidgetInfo.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * The class holds the rendered content and the related information from the render service to
+ * be used to show on the selection toolbar.
+ *
+ * @hide
+ */
+@DataClass(genToString = true, genEqualsHashCode = true)
+public final class WidgetInfo implements Parcelable {
+
+ /**
+ * The token that is used to identify the selection toolbar.
+ */
+ private final long mWidgetToken;
+
+ // TODO: add members when the code really uses it
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/WidgetInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new WidgetInfo.
+ *
+ * @param widgetToken
+ * The token that is used to identify the selection toolbar.
+ */
+ @DataClass.Generated.Member
+ public WidgetInfo(
+ long widgetToken) {
+ this.mWidgetToken = widgetToken;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The token that is used to identify the selection toolbar.
+ */
+ @DataClass.Generated.Member
+ public long getWidgetToken() {
+ return mWidgetToken;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "WidgetInfo { " +
+ "widgetToken = " + mWidgetToken +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(WidgetInfo other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ WidgetInfo that = (WidgetInfo) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mWidgetToken == that.mWidgetToken;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Long.hashCode(mWidgetToken);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeLong(mWidgetToken);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ WidgetInfo(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ long widgetToken = in.readLong();
+
+ this.mWidgetToken = widgetToken;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<WidgetInfo> CREATOR
+ = new Parcelable.Creator<WidgetInfo>() {
+ @Override
+ public WidgetInfo[] newArray(int size) {
+ return new WidgetInfo[size];
+ }
+
+ @Override
+ public WidgetInfo createFromParcel(@NonNull android.os.Parcel in) {
+ return new WidgetInfo(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1639488254020L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/WidgetInfo.java",
+ inputSignatures = "private final long mWidgetToken\nclass WidgetInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 96952089ef51..60ce65153890 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1849,7 +1849,7 @@ public class Editor {
if (mHasPendingRestartInputForSetText) {
final InputMethodManager imm = getInputMethodManager();
if (imm != null) {
- imm.restartInput(mTextView);
+ imm.invalidateInput(mTextView);
}
mHasPendingRestartInputForSetText = false;
}
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index a833600e1fbc..022d05da8825 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -66,4 +66,7 @@ interface ITaskOrganizerController {
* Restarts the top activity in the given task by killing its process if it is visible.
*/
void restartTaskTopActivityProcessIfVisible(in WindowContainerToken task);
+
+ /** Updates a state of camera compat control for stretched issues in the viewfinder. */
+ void updateCameraCompatControlState(in WindowContainerToken task, int state);
}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 27c7d3158f95..3ec18dbe0ebc 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -24,6 +24,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityManager;
+import android.app.TaskInfo.CameraCompatControlState;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.SurfaceControl;
@@ -238,6 +239,20 @@ public class TaskOrganizer extends WindowOrganizer {
}
/**
+ * Updates a state of camera compat control for stretched issues in the viewfinder.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+ public void updateCameraCompatControlState(@NonNull WindowContainerToken task,
+ @CameraCompatControlState int state) {
+ try {
+ mTaskOrganizerController.updateCameraCompatControlState(task, state);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the executor to run callbacks on.
* @hide
*/
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index c9baf004d798..f09e176beea0 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -22,39 +22,46 @@ import android.content.Intent;
import android.content.pm.PackageInfo;
import android.os.ParcelFileDescriptor;
+import com.android.internal.backup.ITransportStatusCallback;
+import com.android.internal.infra.AndroidFuture;
+
/** {@hide} */
-interface IBackupTransport {
+oneway interface IBackupTransport {
/**
- * Ask the transport for the name under which it should be registered. This will
+ * Ask the transport for the String name under which it should be registered. This will
* typically be its host service's component name, but need not be.
+ *
+ * @param resultFuture an {@link AndroidFuture} that is completed with the {@code String} name
+ * of the transport.
+ */
+ void name(in AndroidFuture<String> result);
+
+ /**
+ * Ask the transport for an Intent that can be used to launch any internal
+ * configuration Activity that it wishes to present. For example, the transport
+ * may offer a UI for allowing the user to supply login credentials for the
+ * transport's off-device backend.
+ *
+ * If the transport does not supply any user-facing configuration UI, it should
+ * return null from this method.
+ *
+ * @param resultFuture an {@link AndroidFuture} that is completed with an {@code Intent} that
+ * can be passed to Context.startActivity() in order to launch the transport's
+ * configuration UI. This future will complete with null if the transport does not
+ * offer any user-facing configuration UI.
+ */
+ void configurationIntent(in AndroidFuture<Intent> resultFuture);
+
+ /**
+ * Ask the transport for a one-line string that can be shown to the user that
+ * describes the current backend destination. For example, a transport that
+ * can potentially associate backup data with arbitrary user accounts should
+ * include the name of the currently-active account here.
+ *
+ * @param resultFuture an {@link AndroidFuture} that is completed with a {@code String}
+ * describing the destination to which the transport is currently sending data.
*/
- String name();
-
- /**
- * Ask the transport for an Intent that can be used to launch any internal
- * configuration Activity that it wishes to present. For example, the transport
- * may offer a UI for allowing the user to supply login credentials for the
- * transport's off-device backend.
- *
- * If the transport does not supply any user-facing configuration UI, it should
- * return null from this method.
- *
- * @return An Intent that can be passed to Context.startActivity() in order to
- * launch the transport's configuration UI. This method will return null
- * if the transport does not offer any user-facing configuration UI.
- */
- Intent configurationIntent();
-
- /**
- * On demand, supply a one-line string that can be shown to the user that
- * describes the current backend destination. For example, a transport that
- * can potentially associate backup data with arbitrary user accounts should
- * include the name of the currently-active account here.
- *
- * @return A string describing the destination to which the transport is currently
- * sending data. This method should not return null.
- */
- String currentDestinationString();
+ void currentDestinationString(in AndroidFuture<String> resultFuture);
/**
* Ask the transport for an Intent that can be used to launch a more detailed
@@ -71,22 +78,23 @@ interface IBackupTransport {
* <p>If the transport does not supply any user-facing data management
* UI, then it should return {@code null} from this method.
*
- * @return An intent that can be passed to Context.startActivity() in order to
- * launch the transport's data-management UI. This method will return
- * {@code null} if the transport does not offer any user-facing data
- * management UI.
+ * @param resultFuture an {@link AndroidFuture} that is completed with an {@code Intent} that
+ * can be passed to Context.startActivity() in order to launch the transport's
+ * data-management UI. The callback will supply {@code null} if the transport does not
+ * offer any user-facing data management UI.
*/
- Intent dataManagementIntent();
+ void dataManagementIntent(in AndroidFuture<Intent> resultFuture);
/**
- * On demand, supply a short {@link CharSequence} that can be shown to the user as the label on
- * an overflow menu item used to invoke the data management UI.
+ * Ask the transport for a short {@link CharSequence} that can be shown to the user as the label
+ * on an overflow menu item used to invoke the data management UI.
*
- * @return A {@link CharSequence} to be used as the label for the transport's data management
- * affordance. If the transport supplies a data management intent, this
- * method must not return {@code null}.
+ * @param resultFuture an {@link AndroidFuture} that is completed with a {@code CharSequence}
+ * to be used as the label for the transport's data management affordance. If the
+ * transport supplies a data management Intent via {@link #dataManagementIntent},
+ * this method must not return {@code null}.
*/
- CharSequence dataManagementIntentLabel();
+ void dataManagementIntentLabel(in AndroidFuture<CharSequence> resultFuture);
/**
* Ask the transport where, on local device storage, to keep backup state blobs.
@@ -96,11 +104,11 @@ interface IBackupTransport {
* available backup transports; the name of the class implementing the transport
* is a good choice. This MUST be constant.
*
- * @return A unique name, suitable for use as a file or directory name, that the
- * Backup Manager could use to disambiguate state files associated with
- * different backup transports.
+ * @param resultFuture an {@link AndroidFuture} that is completed with a unique {@code String}
+ * name, suitable for use as a file or directory name, that the Backup Manager could use
+ * to disambiguate state files associated with different backup transports.
*/
- String transportDirName();
+ void transportDirName(in AndroidFuture<String> resultFuture);
/**
* Verify that this is a suitable time for a backup pass. This should return zero
@@ -110,10 +118,11 @@ interface IBackupTransport {
* <p>If this is not a suitable time for a backup, the transport should return a
* backoff delay, in milliseconds, after which the Backup Manager should try again.
*
- * @return Zero if this is a suitable time for a backup pass, or a positive time delay
- * in milliseconds to suggest deferring the backup pass for a while.
+ * @param resultFuture an {@link AndroidFuture} that is completed with {@code int}: zero if
+ * this is a suitable time for a backup pass, or a positive time delay in milliseconds
+ * to suggest deferring the backup pass for a while.
*/
- long requestBackupTime();
+ void requestBackupTime(in AndroidFuture<long> resultFuture);
/**
* Initialize the server side storage for this device, erasing all stored data.
@@ -121,10 +130,11 @@ interface IBackupTransport {
* this is called, {@link #finishBackup} must be called to ensure the request
* is sent and received successfully.
*
- * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far) or
- * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure).
+ * @param callback a callback that is completed with a {@code int} which is one
+ * of {@link BackupConstants#TRANSPORT_OK} (OK so far) or
+ * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure).
*/
- int initializeDevice();
+ void initializeDevice(in ITransportStatusCallback callback);
/**
* Send one application's data to the backup destination. The transport may send
@@ -137,12 +147,14 @@ interface IBackupTransport {
* BackupService.doBackup() method. This may be a pipe rather than a file on
* persistent media, so it may not be seekable.
* @param flags Some of {@link BackupTransport#FLAG_USER_INITIATED}.
- * @return one of {@link BackupConstants#TRANSPORT_OK} (OK so far),
- * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure), or
- * {@link BackupConstants#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has
- * become lost due to inactive expiry or some other reason and needs re-initializing)
+ * @param callback a callback that is completed with a {@code int} which is one
+ * of {@link BackupConstants#TRANSPORT_OK}(OK so far), {@link BackupConstants#TRANSPORT_ERROR}
+ * (on network error or other failure), or {@link BackupConstants#TRANSPORT_NOT_INITIALIZED}
+ * (if the backend dataset has become lost due to inactive expiry or some other reason and
+ * needs re-initializing).
*/
- int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd, int flags);
+ void performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd, int flags,
+ in ITransportStatusCallback callback);
/**
* Erase the give application's data from the backup destination. This clears
@@ -150,9 +162,10 @@ interface IBackupTransport {
* the app had never yet been backed up. After this is called, {@link finishBackup}
* must be called to ensure that the operation is recorded successfully.
*
- * @return the same error codes as {@link #performBackup}.
+ * @param callback a callback that is completed with the same error codes as
+ * {@link #performBackup}.
*/
- int clearBackupData(in PackageInfo packageInfo);
+ void clearBackupData(in PackageInfo packageInfo, in ITransportStatusCallback callback);
/**
* Finish sending application data to the backup destination. This must be
@@ -160,27 +173,30 @@ interface IBackupTransport {
* all data is sent. Only when this method returns true can a backup be assumed
* to have succeeded.
*
- * @return the same error codes as {@link #performBackup}.
+ * @param callback a callback that is completed with the same error codes as
+ * {@link #performBackup}.
*/
- int finishBackup();
+ void finishBackup(in ITransportStatusCallback callback);
/**
* Get the set of all backups currently available over this transport.
*
- * @return Descriptions of the set of restore images available for this device,
- * or null if an error occurred (the attempt should be rescheduled).
+ * @param resultFuture an {@link AndroidFuture} that is completed with {@code List<RestoreSet>}:
+ * the descriptions of a set of restore images available for this device, or null if an
+ * error occurred (the attempt should be rescheduled).
**/
- RestoreSet[] getAvailableRestoreSets();
+ void getAvailableRestoreSets(in AndroidFuture<List<RestoreSet>> resultFuture);
/**
* Get the identifying token of the backup set currently being stored from
* this device. This is used in the case of applications wishing to restore
* their last-known-good data.
*
- * @return A token that can be passed to {@link #startRestore}, or 0 if there
- * is no backup set available corresponding to the current device state.
+ * @param resultFuture an {@link AndroidFuture} that is completed with a {@code long}: a token
+ * that can be passed to {@link #startRestore}, or 0 if there is no backup set available
+ * corresponding to the current device state.
*/
- long getCurrentRestoreSet();
+ void getCurrentRestoreSet(in AndroidFuture<long> resultFuture);
/**
* Start restoring application data from backup. After calling this function,
@@ -191,11 +207,12 @@ interface IBackupTransport {
* or {@link #getCurrentRestoreSet}.
* @param packages List of applications to restore (if data is available).
* Application data will be restored in the order given.
- * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far, call
- * {@link #nextRestorePackage}) or {@link BackupConstants#TRANSPORT_ERROR}
- * (an error occurred, the restore should be aborted and rescheduled).
+ * @param callback a callback that is completed with one of
+ * {@link BackupConstants#TRANSPORT_OK} (OK so far, call {@link #nextRestorePackage}) or
+ * {@link BackupConstants#TRANSPORT_ERROR} (an error occurred, the restore should be aborted
+ * and rescheduled).
*/
- int startRestore(long token, in PackageInfo[] packages);
+ void startRestore(long token, in PackageInfo[] packages, in ITransportStatusCallback callback);
/**
* Get the package name of the next application with data in the backup store, plus
@@ -210,34 +227,95 @@ interface IBackupTransport {
* <p>If this method returns {@code null}, it means that a transport-level error has
* occurred and the entire restore operation should be abandoned.
*
- * @return A RestoreDescription object containing the name of one of the packages
- * supplied to {@link #startRestore} plus an indicator of the data type of that
- * restore data; or {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that
- * no more packages can be restored in this session; or {@code null} to indicate
- * a transport-level error.
+ * @param resultFuture an {@link AndroidFuture} that is completed with a
+ * {@link RestoreDescription} object containing the name of one of the packages supplied to
+ * {@link #startRestore} plus an indicator of the data type of that restore data; or
+ * {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that no more packages can be
+ * restored in this session; or {@code null} to indicate a transport-level error.
*/
- RestoreDescription nextRestorePackage();
+ void nextRestorePackage(in AndroidFuture<RestoreDescription> resultFuture);
/**
* Get the data for the application returned by {@link #nextRestorePackage}.
* @param data An open, writable file into which the backup data should be stored.
- * @return the same error codes as {@link #startRestore}.
+ *
+ * @param callback a callback that is completed with the same error codes as
+ * {@link #startRestore}.
*/
- int getRestoreData(in ParcelFileDescriptor outFd);
+ void getRestoreData(in ParcelFileDescriptor outFd, in ITransportStatusCallback callback);
/**
* End a restore session (aborting any in-process data transfer as necessary),
* freeing any resources and connections used during the restore process.
+ *
+ * @param callback a callback to signal that restore has been finished on transport side.
*/
- void finishRestore();
+ void finishRestore(in ITransportStatusCallback callback);
// full backup stuff
- long requestFullBackupTime();
- int performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket, int flags);
- int checkFullBackupSize(long size);
- int sendBackupData(int numBytes);
- void cancelFullBackup();
+ /**
+ * Verify that this is a suitable time for a full-data backup pass.
+ *
+ * @param resultFuture an {@link AndroidFuture} that is completed with {@code long}: 0 if this
+ * is a suitable time for a backup pass, or a positive time delay in milliseconds to
+ * suggest deferring the backup pass for a while.
+ */
+ void requestFullBackupTime(in AndroidFuture<long> resultFuture);
+
+ /**
+ * Begin the process of sending an application's full-data archive to the backend.
+ *
+ * @param targetPackage The package whose data is to follow.
+ * @param socket The socket file descriptor through which the data will be provided.
+ * @param flags {@link BackupTransport#FLAG_USER_INITIATED} or 0.
+ * @param callback callback to return a {@code int} which is one of:
+ * {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} to indicate that the stated
+ * application is not to be backed up; {@link BackupTransport#TRANSPORT_OK} to indicate
+ * that the OS may proceed with delivering backup data;
+ * {@link BackupTransport#TRANSPORT_ERROR to indicate a fatal error condition that
+ * precludes performing a backup at this time.
+ */
+ void performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket, int flags,
+ in ITransportStatusCallback callback);
+
+ /**
+ * Called after {@link #performFullBackup} to make sure that the transport is willing to
+ * handle a full-data backup operation of the specified size on the current package.
+ *
+ * @param size The estimated size of the full-data payload for this app. This includes
+ * manifest and archive format overhead, but is not guaranteed to be precise.
+ * @param callback a callback that is completed with a {@code int} which is
+ * one of: {@link BackupTransport#TRANSPORT_OK} if the platform is to proceed with the
+ * full-data {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} if the proposed payload
+ * size is backup, too large for the transport to handle, or
+ * {@link BackupTransport#TRANSPORT_ERROR} to indicate a fatal error condition that
+ * means the platform cannot perform a backup at this time.
+ */
+ void checkFullBackupSize(long size, in ITransportStatusCallback callback);
+
+ /**
+ * Tells the transport to read {@code numBytes} bytes of data from the socket file
+ * descriptor provided in the {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)}
+ * call, and deliver those bytes to the datastore.
+ *
+ * @param numBytes The number of bytes of tarball data available to be read from the
+ * socket.
+ * @param callback a callback that is completed with a {@code int} which is
+ * one of: {@link BackupTransport#TRANSPORT_OK} on successful processing of the data,
+ * {@link BackupTransport#TRANSPORT_ERROR} to indicate a fatal error situation. If an
+ * error is returned, the system will call finishBackup() and stop attempting backups
+ * until after a backoff and retry interval.
+ */
+ void sendBackupData(int numBytes, in ITransportStatusCallback callback);
+
+ /**
+ * Tells the transport to cancel the currently-ongoing full backup operation.
+ *
+ * @param callback a callback to indicate that transport has cancelled the operation,
+ * does not return any value (see {@link ITransportCallback#onVoidReceived}).
+ */
+ void cancelFullBackup(in ITransportStatusCallback callback);
/**
* Ask the transport whether this app is eligible for backup.
@@ -245,9 +323,11 @@ interface IBackupTransport {
* @param targetPackage The identity of the application.
* @param isFullBackup If set, transport should check if app is eligible for full data backup,
* otherwise to check if eligible for key-value backup.
- * @return Whether this app is eligible for backup.
+ * @param resultFuture an {@link AndroidFuture} that is completed with a {@code boolean}
+ * indicating whether this app is eligible for backup.
*/
- boolean isAppEligibleForBackup(in PackageInfo targetPackage, boolean isFullBackup);
+ void isAppEligibleForBackup(in PackageInfo targetPackage, boolean isFullBackup,
+ in AndroidFuture<boolean> resultFuture);
/**
* Ask the transport about current quota for backup size of the package.
@@ -255,9 +335,11 @@ interface IBackupTransport {
* @param packageName ID of package to provide the quota.
* @param isFullBackup If set, transport should return limit for full data backup, otherwise
* for key-value backup.
- * @return Current limit on full data backup size in bytes.
+ * @param resultFuture an {@link AndroidFuture} that is completed with a {@code long}: current
+ * limit on full data backup size in bytes.
*/
- long getBackupQuota(String packageName, boolean isFullBackup);
+ void getBackupQuota(String packageName, boolean isFullBackup,
+ in AndroidFuture<long> resultFuture);
// full restore stuff
@@ -284,13 +366,14 @@ interface IBackupTransport {
* @param socket The file descriptor that the transport will use for delivering the
* streamed archive. The transport must close this socket in all cases when returning
* from this method.
- * @return 0 when no more data for the current package is available. A positive value
- * indicates the presence of that many bytes to be delivered to the app. Any negative
- * return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR},
- * indicating a fatal error condition that precludes further restore operations
- * on the current dataset.
+ * @param callback a callback that is completed with an {@code int}: 0 when
+ * no more data for the current package is available. A positive value indicates the
+ * presence of that many bytes to be delivered to the app. Any negative return value is
+ * treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR}, indicating a fatal error
+ * condition that precludes further restore operations on the current dataset.
*/
- int getNextFullRestoreDataChunk(in ParcelFileDescriptor socket);
+ void getNextFullRestoreDataChunk(in ParcelFileDescriptor socket,
+ in ITransportStatusCallback callback);
/**
* If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
@@ -300,19 +383,21 @@ interface IBackupTransport {
* set being iterated over, or will call {@link #finishRestore()} to shut down the restore
* operation.
*
- * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
- * current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
- * transport-level failure. If the transport reports an error here, the entire restore
- * operation will immediately be finished with no further attempts to restore app data.
+ * @param callback a callback that is completed with {@code int}, which is
+ * one of: {@link #TRANSPORT_OK} if the transport was successful in shutting down the current
+ * stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious transport-level failure.
+ * If the transport reports an error here, the entire restore operation will immediately be
+ * finished with no further attempts to restore app data.
*/
- int abortFullRestore();
+ void abortFullRestore(in ITransportStatusCallback callback);
/**
- * Returns flags with additional information about the transport, which is accessible to the
+ * @param resultFuture an {@link AndroidFuture} that is completed with an {@code int}: flags
+ * with additional information about the transport, which is accessible to the
* {@link android.app.backup.BackupAgent}. This allows the agent to decide what to backup or
* restore based on properties of the transport.
*
* <p>For supported flags see {@link android.app.backup.BackupAgent}.
*/
- int getTransportFlags();
+ void getTransportFlags(in AndroidFuture<int> resultFuture);
}
diff --git a/core/java/com/android/internal/backup/ITransportStatusCallback.aidl b/core/java/com/android/internal/backup/ITransportStatusCallback.aidl
new file mode 100644
index 000000000000..a731480b4c6f
--- /dev/null
+++ b/core/java/com/android/internal/backup/ITransportStatusCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.backup;
+
+/**
+* A callback class for {@link IBackupTransport}
+*
+* {@hide}
+*/
+oneway interface ITransportStatusCallback {
+ /**
+ * Callback for methods that complete with an {@code int} status.
+ */
+ void onOperationCompleteWithStatus(int status);
+
+ /**
+ * Callback for methods that complete without a value.
+ */
+ void onOperationComplete();
+}
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 6c2b330611f3..662ed6b9306b 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -25,6 +25,7 @@ import static com.android.internal.inputmethod.InputConnectionProtoDumper.buildG
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
@@ -69,6 +70,82 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
private static final String TAG = "RemoteInputConnectionImpl";
private static final boolean DEBUG = false;
+ /**
+ * An upper limit of calling {@link InputConnection#endBatchEdit()}.
+ *
+ * <p>This is a safeguard against broken {@link InputConnection#endBatchEdit()} implementations,
+ * which are real as we've seen in Bug 208941904. If the retry count reaches to the number
+ * defined here, we fall back into {@link InputMethodManager#restartInput(View)} as a
+ * workaround.</p>
+ */
+ private static final int MAX_END_BATCH_EDIT_RETRY = 16;
+
+ /**
+ * A lightweight per-process type cache to remember classes that never returns {@code false}
+ * from {@link InputConnection#endBatchEdit()}. The implementation is optimized for simplicity
+ * and speed with accepting false-negatives in {@link #contains(Class)}.
+ */
+ private static final class KnownAlwaysTrueEndBatchEditCache {
+ @Nullable
+ private static volatile Class<?> sElement;
+ @Nullable
+ private static volatile Class<?>[] sArray;
+
+ /**
+ * Query if the specified {@link InputConnection} implementation is known to be broken, with
+ * allowing false-negative results.
+ *
+ * @param klass An implementation class of {@link InputConnection} to be tested.
+ * @return {@code true} if the specified type was passed to {@link #add(Class)}.
+ * Note that there is a chance that you still receive {@code false} even if you
+ * called {@link #add(Class)} (false-negative).
+ */
+ @AnyThread
+ static boolean contains(@NonNull Class<? extends InputConnection> klass) {
+ if (klass == sElement) {
+ return true;
+ }
+ final Class<?>[] array = sArray;
+ if (array == null) {
+ return false;
+ }
+ for (Class<?> item : array) {
+ if (item == klass) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Try to remember the specified {@link InputConnection} implementation as a known bad.
+ *
+ * <p>There is a chance that calling this method can accidentally overwrite existing
+ * cache entries. See the document of {@link #contains(Class)} for details.</p>
+ *
+ * @param klass The implementation class of {@link InputConnection} to be remembered.
+ */
+ @AnyThread
+ static void add(@NonNull Class<? extends InputConnection> klass) {
+ if (sElement == null) {
+ // OK to accidentally overwrite an existing element that was set by another thread.
+ sElement = klass;
+ return;
+ }
+
+ final Class<?>[] array = sArray;
+ final int arraySize = array != null ? array.length : 0;
+ final Class<?>[] newArray = new Class<?>[arraySize + 1];
+ for (int i = 0; i < arraySize; ++i) {
+ newArray[i] = array[i];
+ }
+ newArray[arraySize] = klass;
+
+ // OK to accidentally overwrite an existing array that was set by another thread.
+ sArray = newArray;
+ }
+ }
+
@Retention(SOURCE)
private @interface Dispatching {
boolean cancellable();
@@ -155,32 +232,63 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
mH.post(() -> {
try {
if (isFinished()) {
+ // This is a stale request, which can happen. No need to show a warning
+ // because this situation itself is not an error.
return;
}
final InputConnection ic = getInputConnection();
if (ic == null) {
+ // This is a stale request, which can happen. No need to show a warning
+ // because this situation itself is not an error.
+ return;
+ }
+ final View view = getServedView();
+ if (view == null) {
+ // This is a stale request, which can happen. No need to show a warning
+ // because this situation itself is not an error.
return;
}
- // Clean up composing text and batch edit.
- ic.finishComposingText();
- // Also clean up batch edit.
- while (true) {
- if (!ic.endBatchEdit()) {
- break;
+ final Class<? extends InputConnection> icClass = ic.getClass();
+
+ boolean alwaysTrueEndBatchEditDetected =
+ KnownAlwaysTrueEndBatchEditCache.contains(icClass);
+
+ if (!alwaysTrueEndBatchEditDetected) {
+ // Clean up composing text and batch edit.
+ final boolean supportsBatchEdit = ic.beginBatchEdit();
+ ic.finishComposingText();
+ if (supportsBatchEdit) {
+ // Also clean up batch edit.
+ int retryCount = 0;
+ while (true) {
+ if (!ic.endBatchEdit()) {
+ break;
+ }
+ ++retryCount;
+ if (retryCount > MAX_END_BATCH_EDIT_RETRY) {
+ Log.e(TAG, icClass.getTypeName() + "#endBatchEdit() still"
+ + " returns true even after retrying "
+ + MAX_END_BATCH_EDIT_RETRY + " times. Falling back to"
+ + " InputMethodManager#restartInput(View)");
+ alwaysTrueEndBatchEditDetected = true;
+ KnownAlwaysTrueEndBatchEditCache.add(icClass);
+ break;
+ }
+ }
}
}
- final TextSnapshot textSnapshot = ic.takeSnapshot();
- if (textSnapshot == null) {
- final View view = getServedView();
- if (view == null) {
+ if (!alwaysTrueEndBatchEditDetected) {
+ final TextSnapshot textSnapshot = ic.takeSnapshot();
+ if (textSnapshot != null) {
+ mParentInputMethodManager.doInvalidateInput(this, textSnapshot,
+ nextSessionId);
return;
}
- mParentInputMethodManager.restartInput(view);
- return;
}
- mParentInputMethodManager.doInvalidateInput(this, textSnapshot, nextSessionId);
+
+ mParentInputMethodManager.restartInput(view);
} finally {
mHasPendingInvalidation.set(false);
}
diff --git a/core/java/com/android/internal/net/NetworkUtilsInternal.java b/core/java/com/android/internal/net/NetworkUtilsInternal.java
index 052959abff69..1b6cb29d048d 100644
--- a/core/java/com/android/internal/net/NetworkUtilsInternal.java
+++ b/core/java/com/android/internal/net/NetworkUtilsInternal.java
@@ -74,35 +74,4 @@ public class NetworkUtilsInternal {
return true;
}
-
- /**
- * Safely multiple a value by a rational.
- * <p>
- * Internally it uses integer-based math whenever possible, but switches
- * over to double-based math if values would overflow.
- * @hide
- */
- public static long multiplySafeByRational(long value, long num, long den) {
- if (den == 0) {
- throw new ArithmeticException("Invalid Denominator");
- }
- long x = value;
- long y = num;
-
- // Logic shamelessly borrowed from Math.multiplyExact()
- long r = x * y;
- long ax = Math.abs(x);
- long ay = Math.abs(y);
- if (((ax | ay) >>> 31 != 0)) {
- // Some bits greater than 2^31 that might cause overflow
- // Check the result using the divide operator
- // and check for the special case of Long.MIN_VALUE * -1
- if (((y != 0) && (r / y != x))
- || (x == Long.MIN_VALUE && y == -1)) {
- // Use double math to avoid overflowing
- return (long) (((double) num / den) * value);
- }
- }
- return r / den;
- }
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index c2224b4ab9d1..209c64a02324 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -161,7 +161,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 204;
+ static final int VERSION = 205;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -240,6 +240,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final int[] SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS = {
MeasuredEnergyStats.POWER_BUCKET_CPU,
+ MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
};
// TimeInState counters need NUM_PROCESS_STATE states in order to accommodate
@@ -582,6 +583,8 @@ public class BatteryStatsImpl extends BatteryStats {
int UPDATE_ALL =
UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM | UPDATE_DISPLAY;
+ int UPDATE_ON_PROC_STATE_CHANGE = UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT;
+
@IntDef(flag = true, prefix = "UPDATE_", value = {
UPDATE_CPU,
UPDATE_WIFI,
@@ -608,6 +611,8 @@ public class BatteryStatsImpl extends BatteryStats {
Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis);
/** Schedule removal of UIDs corresponding to a removed user */
Future<?> scheduleCleanupDueToRemovedUser(int userId);
+ /** Schedule a sync because of a process state change */
+ Future<?> scheduleSyncDueToProcessStateChange(long delayMillis);
}
public Handler mHandler;
@@ -1703,7 +1708,6 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
-
private static class TimeMultiStateCounter implements TimeBaseObs {
private final TimeBase mTimeBase;
private final LongMultiStateCounter mCounter;
@@ -1736,10 +1740,6 @@ public class BatteryStatsImpl extends BatteryStats {
mCounter.setEnabled(false, elapsedRealtimeUs / 1000);
}
- public LongMultiStateCounter getCounter() {
- return mCounter;
- }
-
public int getStateCount() {
return mCounter.getStateCount();
}
@@ -1753,8 +1753,8 @@ public class BatteryStatsImpl extends BatteryStats {
return mCounter.updateValue(value, timestampMs);
}
- public void addCount(long delta) {
- mCounter.addCount(delta);
+ private void increment(long increment, long timestampMs) {
+ mCounter.incrementValue(increment, timestampMs);
}
/**
@@ -1764,6 +1764,10 @@ public class BatteryStatsImpl extends BatteryStats {
return mCounter.getCount(procState);
}
+ public long getTotalCountLocked() {
+ return mCounter.getTotalCount();
+ }
+
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCounter=" + mCounter);
}
@@ -7985,7 +7989,7 @@ public class BatteryStatsImpl extends BatteryStats {
LongSamplingCounter[] mNetworkByteActivityCounters;
LongSamplingCounter[] mNetworkPacketActivityCounters;
- LongSamplingCounter mMobileRadioActiveTime;
+ TimeMultiStateCounter mMobileRadioActiveTime;
LongSamplingCounter mMobileRadioActiveCount;
/**
@@ -8195,6 +8199,7 @@ public class BatteryStatsImpl extends BatteryStats {
final int batteryConsumerProcessState =
mapUidProcessStateToBatteryConsumerProcessState(procState);
getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
+ getMobileRadioActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
final MeasuredEnergyStats energyStats =
getOrCreateMeasuredEnergyStatsIfSupportedLocked();
if (energyStats != null) {
@@ -8594,11 +8599,10 @@ public class BatteryStatsImpl extends BatteryStats {
/** Adds the given charge to the given standard power bucket for this uid. */
@GuardedBy("mBsi")
private void addChargeToStandardBucketLocked(long chargeDeltaUC,
- @StandardPowerBucket int powerBucket) {
+ @StandardPowerBucket int powerBucket, long timestampMs) {
final MeasuredEnergyStats measuredEnergyStats =
getOrCreateMeasuredEnergyStatsLocked();
- measuredEnergyStats.updateStandardBucket(powerBucket, chargeDeltaUC,
- mBsi.mClock.elapsedRealtime());
+ measuredEnergyStats.updateStandardBucket(powerBucket, chargeDeltaUC, timestampMs);
}
/** Adds the given charge to the given custom power bucket for this uid. */
@@ -8690,6 +8694,13 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("mBsi")
@Override
+ public long getMobileRadioMeasuredBatteryConsumptionUC(int processState) {
+ return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
+ processState);
+ }
+
+ @GuardedBy("mBsi")
+ @Override
public long getScreenOnMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
}
@@ -9228,9 +9239,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
void noteNetworkActivityLocked(int type, long deltaBytes, long deltaPackets) {
- if (mNetworkByteActivityCounters == null) {
- initNetworkActivityLocked();
- }
+ ensureNetworkActivityLocked();
if (type >= 0 && type < NUM_NETWORK_ACTIVITY_TYPES) {
mNetworkByteActivityCounters[type].addCountLocked(deltaBytes);
mNetworkPacketActivityCounters[type].addCountLocked(deltaPackets);
@@ -9240,14 +9249,25 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- void noteMobileRadioActiveTimeLocked(long batteryUptime) {
- if (mNetworkByteActivityCounters == null) {
- initNetworkActivityLocked();
- }
- mMobileRadioActiveTime.addCountLocked(batteryUptime);
+ void noteMobileRadioActiveTimeLocked(long batteryUptimeDeltaUs, long elapsedTimeMs) {
+ ensureNetworkActivityLocked();
+ getMobileRadioActiveTimeCounter().increment(batteryUptimeDeltaUs, elapsedTimeMs);
mMobileRadioActiveCount.addCountLocked(1);
}
+ private TimeMultiStateCounter getMobileRadioActiveTimeCounter() {
+ if (mMobileRadioActiveTime == null) {
+ final long timestampMs = mBsi.mClock.elapsedRealtime();
+ mMobileRadioActiveTime = new TimeMultiStateCounter(
+ mBsi.mOnBatteryTimeBase, BatteryConsumer.PROCESS_STATE_COUNT, timestampMs);
+ mMobileRadioActiveTime.setState(
+ mapUidProcessStateToBatteryConsumerProcessState(mProcessState),
+ timestampMs);
+ mMobileRadioActiveTime.update(0, timestampMs);
+ }
+ return mMobileRadioActiveTime;
+ }
+
@Override
public boolean hasNetworkActivity() {
return mNetworkByteActivityCounters != null;
@@ -9275,8 +9295,20 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
public long getMobileRadioActiveTime(int which) {
- return mMobileRadioActiveTime != null
- ? mMobileRadioActiveTime.getCountLocked(which) : 0;
+ return getMobileRadioActiveTimeInProcessState(BatteryConsumer.PROCESS_STATE_ANY);
+ }
+
+ @Override
+ public long getMobileRadioActiveTimeInProcessState(
+ @BatteryConsumer.ProcessState int processState) {
+ if (mMobileRadioActiveTime == null) {
+ return 0;
+ }
+ if (processState == BatteryConsumer.PROCESS_STATE_ANY) {
+ return mMobileRadioActiveTime.getTotalCountLocked();
+ } else {
+ return mMobileRadioActiveTime.getCountLocked(processState);
+ }
}
@Override
@@ -9388,18 +9420,17 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- void initNetworkActivityLocked() {
- detachIfNotNull(mNetworkByteActivityCounters);
+ void ensureNetworkActivityLocked() {
+ if (mNetworkByteActivityCounters != null) {
+ return;
+ }
+
mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
- detachIfNotNull(mNetworkPacketActivityCounters);
mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
}
- detachIfNotNull(mMobileRadioActiveTime);
- mMobileRadioActiveTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
- detachIfNotNull(mMobileRadioActiveCount);
mMobileRadioActiveCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
}
@@ -9896,7 +9927,12 @@ public class BatteryStatsImpl extends BatteryStats {
mNetworkByteActivityCounters[i].writeToParcel(out);
mNetworkPacketActivityCounters[i].writeToParcel(out);
}
- mMobileRadioActiveTime.writeToParcel(out);
+ if (mMobileRadioActiveTime != null) {
+ out.writeBoolean(true);
+ mMobileRadioActiveTime.writeToParcel(out);
+ } else {
+ out.writeBoolean(false);
+ }
mMobileRadioActiveCount.writeToParcel(out);
} else {
out.writeInt(0);
@@ -9996,6 +10032,7 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("mBsi")
void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
+ final long timestampMs = mBsi.mClock.elapsedRealtime();
mOnBatteryBackgroundTimeBase.readFromParcel(in);
mOnBatteryScreenOffBackgroundTimeBase.readFromParcel(in);
@@ -10205,7 +10242,14 @@ public class BatteryStatsImpl extends BatteryStats {
mNetworkPacketActivityCounters[i]
= new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
}
- mMobileRadioActiveTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
+ if (in.readBoolean()) {
+ final TimeMultiStateCounter counter =
+ new TimeMultiStateCounter(mBsi.mOnBatteryTimeBase, in, timestampMs);
+ if (counter.getStateCount() == BatteryConsumer.PROCESS_STATE_COUNT) {
+ mMobileRadioActiveTime = counter;
+ }
+ }
+
mMobileRadioActiveCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
} else {
mNetworkByteActivityCounters = null;
@@ -10247,7 +10291,6 @@ public class BatteryStatsImpl extends BatteryStats {
mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(
in, mBsi.mOnBatteryScreenOffTimeBase);
- final long timestampMs = mBsi.mClock.elapsedRealtime();
int stateCount = in.readInt();
if (stateCount != 0) {
final TimeMultiStateCounter counter = new TimeMultiStateCounter(
@@ -11182,6 +11225,9 @@ public class BatteryStatsImpl extends BatteryStats {
onBatteryScreenOffCounter.setState(uidRunningState, elapsedRealtimeMs);
}
+ final int prevBatteryConsumerProcessState =
+ mapUidProcessStateToBatteryConsumerProcessState(mProcessState);
+
mProcessState = uidRunningState;
updateOnBatteryBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
@@ -11191,11 +11237,15 @@ public class BatteryStatsImpl extends BatteryStats {
mapUidProcessStateToBatteryConsumerProcessState(uidRunningState);
getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedRealtimeMs);
+ getMobileRadioActiveTimeCounter()
+ .setState(batteryConsumerProcessState, elapsedRealtimeMs);
final MeasuredEnergyStats energyStats =
getOrCreateMeasuredEnergyStatsIfSupportedLocked();
if (energyStats != null) {
energyStats.setState(batteryConsumerProcessState, elapsedRealtimeMs);
}
+ maybeScheduleExternalStatsSync(prevBatteryConsumerProcessState,
+ batteryConsumerProcessState);
}
if (userAwareService != mInForegroundService) {
@@ -11208,6 +11258,27 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ @GuardedBy("mBsi")
+ private void maybeScheduleExternalStatsSync(
+ @BatteryConsumer.ProcessState int oldProcessState,
+ @BatteryConsumer.ProcessState int newProcessState) {
+ if (oldProcessState == newProcessState) {
+ return;
+ }
+ // Transitions between BACKGROUND and such non-foreground states like cached
+ // or nonexistent do not warrant doing a sync. If some of the stats for those
+ // proc states bleed into the PROCESS_STATE_BACKGROUND, that's ok.
+ if ((oldProcessState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED
+ && newProcessState == BatteryConsumer.PROCESS_STATE_BACKGROUND)
+ || (oldProcessState == BatteryConsumer.PROCESS_STATE_BACKGROUND
+ && newProcessState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED)) {
+ return;
+ }
+
+ mBsi.mExternalSync.scheduleSyncDueToProcessStateChange(
+ mBsi.mConstants.PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
+ }
+
/** Whether to consider Uid to be in the background for background timebase purposes. */
public boolean isInBackground() {
// Note that PROCESS_STATE_CACHED and Uid.PROCESS_STATE_NONEXISTENT is
@@ -12805,7 +12876,8 @@ public class BatteryStatsImpl extends BatteryStats {
.calcGlobalPowerWithoutControllerDataMah(globalTimeMs);
}
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_WIFI,
- consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah);
+ consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah,
+ elapsedRealtimeMs);
}
}
}
@@ -12963,7 +13035,7 @@ public class BatteryStatsImpl extends BatteryStats {
final long appPackets = entry.rxPackets + entry.txPackets;
final long appRadioTimeUs =
(totalAppRadioTimeUs * appPackets) / totalPackets;
- u.noteMobileRadioActiveTimeLocked(appRadioTimeUs);
+ u.noteMobileRadioActiveTimeLocked(appRadioTimeUs, elapsedRealtimeMs);
// Distribute measured mobile radio charge consumption based on app radio
// active time
@@ -13042,7 +13114,7 @@ public class BatteryStatsImpl extends BatteryStats {
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
consumedChargeUC, uidEstimatedConsumptionMah,
- totalEstimatedConsumptionMah);
+ totalEstimatedConsumptionMah, elapsedRealtimeMs);
}
mNetworkStatsPool.release(delta);
@@ -13330,7 +13402,8 @@ public class BatteryStatsImpl extends BatteryStats {
= mBluetoothPowerCalculator.calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
totalEstimatedMah = Math.max(totalEstimatedMah, controllerMaMs / MILLISECONDS_IN_HOUR);
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
- consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedMah);
+ consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedMah,
+ elapsedRealtimeMs);
}
mLastBluetoothActivityInfo.set(info);
@@ -13433,8 +13506,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (totalCpuChargeUC <= 0) return;
+ final long timestampMs = mClock.elapsedRealtime();
+
mGlobalMeasuredEnergyStats.updateStandardBucket(MeasuredEnergyStats.POWER_BUCKET_CPU,
- totalCpuChargeUC, mClock.elapsedRealtime());
+ totalCpuChargeUC, timestampMs);
// Calculate the measured microcoulombs/calculated milliamp-hour charge ratio for each
// cluster to normalize each uid's estimated power usage against actual power usage for
@@ -13483,7 +13558,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid.addChargeToStandardBucketLocked(uidCpuChargeUC,
- MeasuredEnergyStats.POWER_BUCKET_CPU);
+ MeasuredEnergyStats.POWER_BUCKET_CPU, timestampMs);
}
}
@@ -13583,7 +13658,7 @@ public class BatteryStatsImpl extends BatteryStats {
fgTimeUsArray.put(uid.getUid(), (double) fgTimeUs);
}
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON,
- totalScreenOnChargeUC, fgTimeUsArray, 0);
+ totalScreenOnChargeUC, fgTimeUsArray, 0, elapsedRealtimeMs);
}
/**
@@ -13627,7 +13702,7 @@ public class BatteryStatsImpl extends BatteryStats {
gnssTimeUsArray.put(uid.getUid(), (double) gnssTimeUs);
}
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_GNSS, chargeUC,
- gnssTimeUsArray, 0);
+ gnssTimeUsArray, 0, elapsedRealtimeMs);
}
/**
@@ -13699,7 +13774,7 @@ public class BatteryStatsImpl extends BatteryStats {
@SuppressWarnings("GuardedBy") // errorprone false positive on u.addChargeToStandardBucketLocked
private void distributeEnergyToUidsLocked(@StandardPowerBucket int bucket,
long totalConsumedChargeUC, SparseDoubleArray ratioNumerators,
- double minRatioDenominator) {
+ double minRatioDenominator, long timestampMs) {
// If the sum of all app usage was greater than the total, use that instead:
double sumRatioNumerators = 0;
@@ -13714,7 +13789,7 @@ public class BatteryStatsImpl extends BatteryStats {
final double ratioNumerator = ratioNumerators.valueAt(i);
final long uidActualUC
= (long) (totalConsumedChargeUC * ratioNumerator / ratioDenominator + 0.5);
- uid.addChargeToStandardBucketLocked(uidActualUC, bucket);
+ uid.addChargeToStandardBucketLocked(uidActualUC, bucket, timestampMs);
}
}
@@ -14433,7 +14508,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (childUid != null) {
final long delta =
childUid.cpuActiveCounter.update(cpuActiveTimesMs, elapsedRealtimeMs);
- u.getCpuActiveTimeCounter().addCount(delta);
+ u.getCpuActiveTimeCounter().increment(delta, elapsedRealtimeMs);
}
}
});
@@ -15627,7 +15702,6 @@ public class BatteryStatsImpl extends BatteryStats {
for (int procState = 0; procState < BatteryConsumer.PROCESS_STATE_COUNT; procState++) {
procStateNames[procState] = BatteryConsumer.processStateToString(procState);
}
- procStateNames[BatteryConsumer.PROCESS_STATE_ANY] = "untracked";
return procStateNames;
}
@@ -15651,6 +15725,8 @@ public class BatteryStatsImpl extends BatteryStats {
= "external_stats_collection_rate_limit_ms";
public static final String KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS
= "battery_level_collection_delay_ms";
+ public static final String KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS =
+ "procstate_change_collection_delay_ms";
public static final String KEY_MAX_HISTORY_FILES = "max_history_files";
public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
public static final String KEY_BATTERY_CHARGED_DELAY_MS =
@@ -15661,6 +15737,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
private static final long DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = 600_000;
private static final long DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS = 300_000;
+ private static final long DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS = 60_000;
private static final int DEFAULT_MAX_HISTORY_FILES = 32;
private static final int DEFAULT_MAX_HISTORY_BUFFER_KB = 128; /*Kilo Bytes*/
private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
@@ -15676,6 +15753,8 @@ public class BatteryStatsImpl extends BatteryStats {
= DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS;
public long BATTERY_LEVEL_COLLECTION_DELAY_MS
= DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS;
+ public long PROC_STATE_CHANGE_COLLECTION_DELAY_MS =
+ DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS;
public int MAX_HISTORY_FILES;
public int MAX_HISTORY_BUFFER; /*Bytes*/
public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS;
@@ -15742,6 +15821,9 @@ public class BatteryStatsImpl extends BatteryStats {
BATTERY_LEVEL_COLLECTION_DELAY_MS = mParser.getLong(
KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS,
DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS);
+ PROC_STATE_CHANGE_COLLECTION_DELAY_MS = mParser.getLong(
+ KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS,
+ DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
MAX_HISTORY_FILES = mParser.getInt(KEY_MAX_HISTORY_FILES,
ActivityManager.isLowRamDeviceStatic() ?
@@ -15793,6 +15875,8 @@ public class BatteryStatsImpl extends BatteryStats {
pw.println(EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
pw.print(KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS); pw.print("=");
pw.println(BATTERY_LEVEL_COLLECTION_DELAY_MS);
+ pw.print(KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS); pw.print("=");
+ pw.println(PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
pw.print(KEY_MAX_HISTORY_FILES); pw.print("=");
pw.println(MAX_HISTORY_FILES);
pw.print(KEY_MAX_HISTORY_BUFFER_KB); pw.print("=");
@@ -16476,14 +16560,18 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (in.readInt() != 0) {
- if (u.mNetworkByteActivityCounters == null) {
- u.initNetworkActivityLocked();
- }
+ u.ensureNetworkActivityLocked();
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
u.mNetworkByteActivityCounters[i].readSummaryFromParcelLocked(in);
u.mNetworkPacketActivityCounters[i].readSummaryFromParcelLocked(in);
}
- u.mMobileRadioActiveTime.readSummaryFromParcelLocked(in);
+ if (in.readBoolean()) {
+ TimeMultiStateCounter counter = new TimeMultiStateCounter(
+ mOnBatteryTimeBase, in, elapsedRealtimeMs);
+ if (counter.getStateCount() == BatteryConsumer.PROCESS_STATE_COUNT) {
+ u.mMobileRadioActiveTime = counter;
+ }
+ }
u.mMobileRadioActiveCount.readSummaryFromParcelLocked(in);
}
@@ -17033,7 +17121,12 @@ public class BatteryStatsImpl extends BatteryStats {
u.mNetworkByteActivityCounters[i].writeSummaryFromParcelLocked(out);
u.mNetworkPacketActivityCounters[i].writeSummaryFromParcelLocked(out);
}
- u.mMobileRadioActiveTime.writeSummaryFromParcelLocked(out);
+ if (u.mMobileRadioActiveTime != null) {
+ out.writeBoolean(true);
+ u.mMobileRadioActiveTime.writeToParcel(out);
+ } else {
+ out.writeBoolean(false);
+ }
u.mMobileRadioActiveCount.writeSummaryFromParcelLocked(out);
}
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 175f28ffcda9..ee614cdbdb95 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -156,11 +156,11 @@ public class CpuPowerCalculator extends PowerCalculator {
private void calculateMeasuredPowerPerProcessState(UidBatteryConsumer.Builder app,
BatteryStats.Uid u, BatteryConsumer.Key[] keys) {
for (BatteryConsumer.Key key : keys) {
- // The key for "PROCESS_STATE_ANY" has already been populated with the
- // full energy across all states. We don't want to override it with
+ // The key for PROCESS_STATE_UNSPECIFIED aka PROCESS_STATE_ANY has already been
+ // populated with the full energy across all states. We don't want to override it with
// the energy for "other" states, which excludes the tracked states like
// foreground, background etc.
- if (key.processState == BatteryConsumer.PROCESS_STATE_ANY) {
+ if (key.processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
continue;
}
@@ -184,7 +184,7 @@ public class CpuPowerCalculator extends PowerCalculator {
uidProcState++) {
@BatteryConsumer.ProcessState int procState =
BatteryStats.mapUidProcessStateToBatteryConsumerProcessState(uidProcState);
- if (procState == BatteryConsumer.PROCESS_STATE_ANY) {
+ if (procState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
continue;
}
@@ -199,7 +199,7 @@ public class CpuPowerCalculator extends PowerCalculator {
}
for (BatteryConsumer.Key key : keys) {
- if (key.processState == BatteryConsumer.PROCESS_STATE_ANY) {
+ if (key.processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
continue;
}
diff --git a/core/java/com/android/internal/os/LongMultiStateCounter.java b/core/java/com/android/internal/os/LongMultiStateCounter.java
index ad8e0ed4c2f5..33a9d547be78 100644
--- a/core/java/com/android/internal/os/LongMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongMultiStateCounter.java
@@ -122,6 +122,13 @@ public final class LongMultiStateCounter implements Parcelable {
/**
* Adds the supplied values to the current accumulated values in the counter.
*/
+ public void incrementValue(long count, long timestampMs) {
+ native_incrementValue(mNativeObject, count, timestampMs);
+ }
+
+ /**
+ * Adds the supplied values to the current accumulated values in the counter.
+ */
public void addCount(long count) {
native_addCount(mNativeObject, count);
}
@@ -144,6 +151,17 @@ public final class LongMultiStateCounter implements Parcelable {
return native_getCount(mNativeObject, state);
}
+ /**
+ * Returns the total accumulated count across all states.
+ */
+ public long getTotalCount() {
+ long total = 0;
+ for (int state = 0; state < mStateCount; state++) {
+ total += native_getCount(mNativeObject, state);
+ }
+ return total;
+ }
+
@Override
public String toString() {
return native_toString(mNativeObject);
@@ -190,6 +208,10 @@ public final class LongMultiStateCounter implements Parcelable {
private static native long native_updateValue(long nativeObject, long value, long timestampMs);
@CriticalNative
+ private static native void native_incrementValue(long nativeObject, long increment,
+ long timestampMs);
+
+ @CriticalNative
private static native void native_addCount(long nativeObject, long count);
@CriticalNative
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index eb5993dc2d61..28cc836396b4 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -34,6 +34,8 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
private static final int NUM_SIGNAL_STRENGTH_LEVELS =
CellSignalStrength.getNumSignalStrengthLevels();
+ private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
+
private final UsageBasedPowerEstimator mActivePowerEstimator;
private final UsageBasedPowerEstimator[] mIdlePowerEstimators =
new UsageBasedPowerEstimator[NUM_SIGNAL_STRENGTH_LEVELS];
@@ -89,14 +91,22 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
PowerAndDuration total = new PowerAndDuration();
- final double powerPerPacketMah = getMobilePowerPerPacket(batteryStats, rawRealtimeUs,
- BatteryStats.STATS_SINCE_CHARGED);
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
+ BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
+
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
final BatteryStats.Uid uid = app.getBatteryStatsUid();
- calculateApp(app, uid, powerPerPacketMah, total, query);
+ if (keys == UNINITIALIZED_KEYS) {
+ if (query.isProcessStateDataNeeded()) {
+ keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+ } else {
+ keys = null;
+ }
+ }
+
+ calculateApp(app, uid, total, query, keys);
}
final long consumptionUC = batteryStats.getMobileRadioMeasuredBatteryConsumptionUC();
@@ -121,34 +131,49 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
}
private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
- double powerPerPacketMah, PowerAndDuration total,
- BatteryUsageStatsQuery query) {
+ PowerAndDuration total,
+ BatteryUsageStatsQuery query, BatteryConsumer.Key[] keys) {
final long radioActiveDurationMs = calculateDuration(u, BatteryStats.STATS_SINCE_CHARGED);
total.totalAppDurationMs += radioActiveDurationMs;
final long consumptionUC = u.getMobileRadioMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(consumptionUC, query);
- final double powerMah = calculatePower(u, powerModel, powerPerPacketMah,
- radioActiveDurationMs, consumptionUC);
+ final double powerMah = calculatePower(u, powerModel, radioActiveDurationMs, consumptionUC);
total.totalAppPowerMah += powerMah;
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
radioActiveDurationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, powerMah,
powerModel);
+
+ if (query.isProcessStateDataNeeded() && keys != null) {
+ for (BatteryConsumer.Key key: keys) {
+ final int processState = key.processState;
+ if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+ // Already populated with the total across all process states
+ continue;
+ }
+
+ final long durationInStateMs =
+ u.getMobileRadioActiveTimeInProcessState(processState) / 1000;
+ final long consumptionInStateUc =
+ u.getMobileRadioMeasuredBatteryConsumptionUC(processState);
+ final double powerInStateMah = calculatePower(u, powerModel, durationInStateMs,
+ consumptionInStateUc);
+ app.setConsumedPower(key, powerInStateMah, powerModel);
+ }
+ }
}
@Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- final double mobilePowerPerPacket = getMobilePowerPerPacket(batteryStats, rawRealtimeUs,
- statsType);
PowerAndDuration total = new PowerAndDuration();
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper app = sippers.get(i);
if (app.drainType == BatterySipper.DrainType.APP) {
final BatteryStats.Uid u = app.uidObj;
- calculateApp(app, u, statsType, mobilePowerPerPacket, total);
+ calculateApp(app, u, statsType, total);
}
}
@@ -172,13 +197,12 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
}
private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
- double powerPerPacketMah, PowerAndDuration total) {
+ PowerAndDuration total) {
app.mobileActive = calculateDuration(u, statsType);
final long consumptionUC = u.getMobileRadioMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(consumptionUC);
- app.mobileRadioPowerMah = calculatePower(u, powerModel, powerPerPacketMah, app.mobileActive,
- consumptionUC);
+ app.mobileRadioPowerMah = calculatePower(u, powerModel, app.mobileActive, consumptionUC);
total.totalAppDurationMs += app.mobileActive;
// Add cost of mobile traffic.
@@ -205,26 +229,15 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
}
private double calculatePower(BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel,
- double powerPerPacketMah, long radioActiveDurationMs, long measuredChargeUC) {
+ long radioActiveDurationMs, long measuredChargeUC) {
if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
return uCtoMah(measuredChargeUC);
}
if (radioActiveDurationMs > 0) {
- // We are tracking when the radio is up, so can use the active time to
- // determine power use.
return calcPowerFromRadioActiveDurationMah(radioActiveDurationMs);
- } else {
- // We are not tracking when the radio is up, so must approximate power use
- // based on the number of packets.
- final long mobileRxPackets = u.getNetworkActivityPackets(
- BatteryStats.NETWORK_MOBILE_RX_DATA,
- BatteryStats.STATS_SINCE_CHARGED);
- final long mobileTxPackets = u.getNetworkActivityPackets(
- BatteryStats.NETWORK_MOBILE_TX_DATA,
- BatteryStats.STATS_SINCE_CHARGED);
- return (mobileRxPackets + mobileTxPackets) * powerPerPacketMah;
}
+ return 0;
}
private void calculateRemaining(PowerAndDuration total,
@@ -299,21 +312,4 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
public double calcScanTimePowerMah(long scanningTimeMs) {
return mScanPowerEstimator.calculatePower(scanningTimeMs);
}
-
- /**
- * Return estimated power (in mAh) of sending or receiving a packet with the mobile radio.
- */
- private double getMobilePowerPerPacket(BatteryStats stats, long rawRealtimeUs, int statsType) {
- final long radioDataUptimeMs =
- stats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
- final double mobilePower = calcPowerFromRadioActiveDurationMah(radioDataUptimeMs);
-
- final long mobileRx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
- statsType);
- final long mobileTx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
- statsType);
- final long mobilePackets = mobileRx + mobileTx;
-
- return mobilePackets != 0 ? mobilePower / mobilePackets : 0;
- }
}
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index 4dc9aa5228d6..a52ae107d983 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -589,6 +589,10 @@ public class MeasuredEnergyStats {
final int numIndices = mConfig.getNumberOfBuckets();
for (int index = 0; index < numIndices; index++) {
setValueIfSupported(index, 0L);
+ if (mAccumulatedMultiStateChargeMicroCoulomb != null
+ && mAccumulatedMultiStateChargeMicroCoulomb[index] != null) {
+ mAccumulatedMultiStateChargeMicroCoulomb[index].reset();
+ }
}
}
diff --git a/core/java/com/android/internal/telephony/ICarrierPrivilegesListener.aidl b/core/java/com/android/internal/telephony/ICarrierPrivilegesListener.aidl
new file mode 100644
index 000000000000..6ca8cecba3c8
--- /dev/null
+++ b/core/java/com/android/internal/telephony/ICarrierPrivilegesListener.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+oneway interface ICarrierPrivilegesListener {
+ void onCarrierPrivilegesChanged(
+ in List<String> privilegedPackageNames, in int[] privilegedUids);
+}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 6ba0279313b1..9712d7e38a4b 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -32,6 +32,7 @@ import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.emergency.EmergencyNumber;
+import com.android.internal.telephony.ICarrierPrivilegesListener;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -77,6 +78,7 @@ interface ITelephonyRegistry {
void notifySubscriptionInfoChanged();
void notifyOpportunisticSubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
+ void notifyCarrierNetworkChangeWithSubId(in int subId, in boolean active);
void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
void notifyDisplayInfoChanged(int slotIndex, int subId, in TelephonyDisplayInfo telephonyDisplayInfo);
void notifyPhoneCapabilityChanged(in PhoneCapability capability);
@@ -99,4 +101,10 @@ interface ITelephonyRegistry {
void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType);
void notifyLinkCapacityEstimateChanged(in int phoneId, in int subId,
in List<LinkCapacityEstimate> linkCapacityEstimateList);
+
+ void addCarrierPrivilegesListener(
+ int phoneId, ICarrierPrivilegesListener callback, String pkg, String featureId);
+ void removeCarrierPrivilegesListener(ICarrierPrivilegesListener callback, String pkg);
+ void notifyCarrierPrivilegesChanged(
+ int phoneId, in List<String> privilegedPackageNames, in int[] privilegedUids);
}
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index b910d1664ad1..74bbd7b2cb32 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -269,8 +269,7 @@ int register_android_view_InputQueue(JNIEnv* env)
return RegisterMethodsOrDie(env, kInputQueuePathName, g_methods, NELEM(g_methods));
}
-AInputQueue* android_view_InputQueue_getNativePtr(jobject inputQueue) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+AInputQueue* android_view_InputQueue_getNativePtr(JNIEnv* env, jobject inputQueue) {
jlong ptr = env->CallLongMethod(inputQueue, gInputQueueClassInfo.getNativePtr);
return reinterpret_cast<AInputQueue*>(ptr);
}
diff --git a/core/jni/com_android_internal_os_LongMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
index 45652a79a5c6..69c4f3ddd9c5 100644
--- a/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
@@ -84,6 +84,10 @@ static jlong native_updateValue(jlong nativePtr, jlong value, jlong timestamp) {
return (jlong)asLongMultiStateCounter(nativePtr)->updateValue((int64_t)value, timestamp);
}
+static void native_incrementValue(jlong nativePtr, jlong count, jlong timestamp) {
+ asLongMultiStateCounter(nativePtr)->incrementValue(count, timestamp);
+}
+
static void native_addCount(jlong nativePtr, jlong count) {
asLongMultiStateCounter(nativePtr)->addValue(count);
}
@@ -172,6 +176,8 @@ static const JNINativeMethod g_methods[] = {
// @CriticalNative
{"native_updateValue", "(JJJ)J", (void *)native_updateValue},
// @CriticalNative
+ {"native_incrementValue", "(JJJ)V", (void *)native_incrementValue},
+ // @CriticalNative
{"native_addCount", "(JJ)V", (void *)native_addCount},
// @CriticalNative
{"native_reset", "(J)V", (void *)native_reset},
diff --git a/core/jni/include/android_runtime/android_view_InputQueue.h b/core/jni/include/android_runtime/android_view_InputQueue.h
index c1b611cf303e..115e2f8605b0 100644
--- a/core/jni/include/android_runtime/android_view_InputQueue.h
+++ b/core/jni/include/android_runtime/android_view_InputQueue.h
@@ -80,7 +80,7 @@ private:
Vector<key_value_pair_t<InputEvent*, bool> > mFinishedEvents;
};
-extern AInputQueue* android_view_InputQueue_getNativePtr(jobject inputQueue);
+extern AInputQueue* android_view_InputQueue_getNativePtr(JNIEnv* env, jobject inputQueue);
} // namespace android
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 55c34fc1b891..420c7c8a8a34 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3997,6 +3997,14 @@
<permission android:name="android.permission.BIND_TEXTCLASSIFIER_SERVICE"
android:protectionLevel="signature" />
+ <!-- Must be required by a android.service.selectiontoolbar.SelectionToolbarRenderService,
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_SELECTION_TOOLBAR_RENDER_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a android.service.contentcapture.ContentCaptureService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
@@ -6057,6 +6065,19 @@
<permission android:name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP"
android:protectionLevel="signature|role" />
+ <!-- @SystemApi Allows an application to update certain device management related system
+ resources.
+ @hide -->
+ <permission android:name="android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows an app to read whether SafetyCenter is enabled/disabled.
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.READ_SAFETY_CENTER_STATUS"
+ android:protectionLevel="signature|privileged" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
@@ -6558,6 +6579,16 @@
</intent-filter>
</service>
+ <!-- TODO: Move to ExtServices or relevant component. -->
+ <service android:name="com.android.server.selectiontoolbar.DefaultSelectionToolbarRenderService"
+ android:permission="android.permission.BIND_SELECTION_TOOLBAR_RENDER_SERVICE"
+ android:process=":ui"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.service.selectiontoolbar.SelectionToolbarRenderService"/>
+ </intent-filter>
+ </service>
+
<provider
android:name="com.android.server.textclassifier.IconsContentProvider"
android:authorities="com.android.textclassifier.icons"
diff --git a/core/res/res/drawable/ic_add_supervised_user.xml b/core/res/res/drawable/ic_add_supervised_user.xml
new file mode 100644
index 000000000000..a49377594a22
--- /dev/null
+++ b/core/res/res/drawable/ic_add_supervised_user.xml
@@ -0,0 +1,34 @@
+<!--
+ ~ 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="40dp"
+ android:height="40dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20"
+ android:tint="?android:attr/colorControlNormal">
+
+ <group
+ android:scaleX="0.5"
+ android:scaleY="0.5">
+
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M15.625,22.5q-2.375,0 -4.063,-1.688 -1.687,-1.687 -1.687,-4.104 0,-2.375 1.688,-4.062 1.687,-1.688 4.062,-1.688 2.417,0 4.105,1.688 1.687,1.687 1.687,4.062 0,2.417 -1.688,4.105 -1.687,1.687 -4.104,1.687zM15.625,19.708q1.292,0 2.146,-0.875 0.854,-0.875 0.854,-2.125t-0.854,-2.125q-0.854,-0.875 -2.146,-0.875 -1.208,0 -2.104,0.875 -0.896,0.875 -0.896,2.125t0.896,2.125q0.896,0.875 2.104,0.875zM27.875,24.333q-1.792,0 -3.063,-1.27 -1.27,-1.271 -1.27,-3.063 0,-1.792 1.27,-3.063 1.271,-1.27 3.063,-1.27 1.833,0 3.083,1.27 1.25,1.271 1.25,3.063 0,1.792 -1.25,3.063 -1.25,1.27 -3.083,1.27zM17.458,33.667q1.959,-3.75 5.063,-5.063 3.104,-1.312 5.354,-1.312 0.958,0 1.813,0.145 0.854,0.146 1.729,0.396 1.041,-1.541 1.75,-3.583 0.708,-2.042 0.708,-4.25 0,-5.792 -4.041,-9.834Q25.791,6.125 20,6.125t-9.834,4.041Q6.125,14.208 6.125,20q0,2.042 0.563,3.938 0.562,1.895 1.604,3.437 1.625,-0.833 3.52,-1.333 1.896,-0.5 3.813,-0.5 1.208,0 2.333,0.188 1.125,0.187 2.125,0.52 -0.833,0.458 -1.562,1 -0.729,0.542 -1.354,1.125 -0.459,-0.042 -0.813,-0.042h-0.729q-1.375,0 -2.916,0.355 -1.542,0.354 -2.751,0.937 1.5,1.583 3.438,2.645 1.937,1.063 4.062,1.397zM20,36.667q-3.417,0 -6.459,-1.313 -3.041,-1.312 -5.312,-3.583 -2.271,-2.271 -3.583,-5.313Q3.333,23.418 3.333,20q0,-3.458 1.313,-6.479Q5.958,10.5 8.229,8.229t5.313,-3.583Q16.582,3.333 20,3.333q3.458,0 6.479,1.313 3.021,1.312 5.292,3.583t3.584,5.292q1.312,3.021 1.312,6.479 0,3.417 -1.313,6.459 -1.312,3.041 -3.583,5.312 -2.271,2.271 -5.292,3.584 -3.021,1.312 -6.479,1.312z"/>
+
+ </group>
+
+</vector>
+
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index c02981e643db..585c372da5fe 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Laat die program toe om die kitsboodskapdiens te gebruik om oproepe sonder jou ingryping te maak."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lees foonstatus en identiteit"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Laat die program toe om toegang tot die foonfunksies van die toestel te verkry. Hierdie toestemming laat die program toe om te bepaal wat die foonnommer en toestel-IDs is, of die oproep aan die gang is, en die afgeleë nommer wat deur \'n oproep verbind word."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lees basiese telefoniestatus en -identiteit"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gee die program toegang tot die toestel se basiese telefoniekenmerke."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"roeteer oproepe deur die stelsel"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Laat die program toe om sy oproepe deur die stelsel te stuur om die oproepervaring te verbeter."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"sien en beheer oproepe deur die stelsel."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tik om jou gesigmodel uit te vee en voeg jou gesig dan weer by"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Stel Gesigslot op"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ontsluit jou foon deur daarna te kyk"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Skakel "<b>"kameratoegang"</b>" in Instellings &gt; Privaatheid aan om Gesigslot te gebruik"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Stel meer maniere op om te ontsluit"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tik om \'n vingerafdruk by te voeg"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Vingerafdrukslot"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Berei tans <xliff:g id="APPNAME">%1$s</xliff:g> voor."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Begin programme."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Voltooi herlaai."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Skakel skerm af?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Jy het die aan/af-skakelaar gedruk terwyl jy jou vingerafdruk gestel het.\n\nDit skakel gewoonlik jou skerm af."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Skakel af"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Kanselleer"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Gaan voort met opstelling?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Jy het die aan/af-skakelaar gedruk – dit skakel gewoonlik die skerm af.\n\nProbeer liggies tik terwyl jy jou vingerafdruk opstel."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Skakel skerm af"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Doen opstelling"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Gaan voort met vingerafdrukverifiëring?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Jy het die aan/af-skakelaar gedruk – dit skakel gewoonlik die skerm af.\n\nProbeer liggies tik om jou vingerafdruk te verifieer."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Skakel skerm af"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Gaan voort"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> loop"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tik om na die speletjie terug te keer"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Kies speletjie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index be4eeb8c693c..f338166ce87f 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"መተግበሪያው ያለእርስዎ ጣልቃ ገብነት ጥሪዎችን ለማድረግ የአይኤምኤስ አገልግሎቱን እንዲጠቀም ያስችለዋል።"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"የስልክ ሁኔታና ማንነት አንብብ"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"መተግበሪያው የመሳሪያውን የስልክ ባህሪያት ላይ እንዲደርስ ይፈቅድለታል። ይህ ፈቃድ መተግበሪያው የስልክ ቁጥሩን እና የመሳሪያውን መታወቂያዎች፣ ጥሪ የነቃ እንደሆነ፣ እና በጥሪ የተገናኘውን የሩቅ ቁጥር እንዲወስን ይፈቅድለታል።"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"መሠረታዊ የቴሌፎኒ ሁኔታ እና ማንነት ያንብቡ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"መተግበሪያው የመሣሪያውን መሠረታዊ የቴሌፎኒ ባህሪያት እንዲደርስ ይፈቅድለታል።"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ጥሪዎችን በስርዓቱ በኩል አዙር"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"መተግበሪያው የጥሪ ተሞክሮን እንዲያሻሽል ጥሪዎቹን በስርዓቱ በኩል እንዲያዞር ያስችለዋል።"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"በሥርዓቱ በኩል ጥሪዎችን ይመልከቱ እና ይቆጣጠሩ።"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"የእርስዎ የመልክ ሞዴል ለመሰረዝ መታ ያድርጉ፣ ከዚያ መልክዎን እንደገና ያክሉ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"በመልክ መክፈትን ያዋቅሩ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ስልክዎን በመመልከት ያስከፍቱት"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"በመልክ መክፈትን ለመጠቀም "<b>"የካሜራ መዳረሻ"</b>"ን በቅንብሮች እና ግላዊነት ውስጥ ያብሩ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"የሚከፍቱባቸው ተጨማሪ መንገዶችን ያቀናብሩ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"የጣት አሻራን ለማከል መታ ያድርጉ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"በጣት አሻራ መክፈቻ"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>ን ማዘጋጀት።"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"መተግበሪያዎችን በማስጀመር ላይ፡፡"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"አጨራረስ ማስነሻ፡፡"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ማያ ገጽ ይጥፋ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"የጣት አሻራዎን ሲያዋቅሩ የኃይል አዝራሩን ተጫንተውታል። \n\n ይህ አብዛኛው ጊዜ ማያ ገጽዎን ያጠፈዋል።"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"አጥፋ"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ይቅር"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ማዋቀር ይቀጥሉ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"የማብሪያ/ማጥፊያ ቁልፉን ተጭነዋል — ይህ ብዙውን ጊዜ ማያ ገጹን ያጠፋል።\n\nየጣት አሻራዎን በሚያዋቅሩበት ጊዜ በትንሹ መታ ለማድረግ ይሞክሩ።"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ማያ ገጽን አጥፋ"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ማዋቀር ቀጥል"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"የጣት አሻራዎን ማረጋገጥ ይቀጥሉ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"የማብሪያ/ማጥፊያ ቁልፉን ተጭነዋል - ይህ ብዙውን ጊዜ ማያ ገጹን ያጠፋል። \n\n የጣት አሻራዎን ለማረጋገጥ በትንሹ መታ ለማድረግ ይሞክሩ።"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ማያ ገጽን አጥፋ"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ቀጥል"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> አሂድ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ወደ ጨዋታ ለመመለስ መታ ያድርጉ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ጨዋታ ይምረጡ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 0748af27ed9a..c15f298740c4 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -483,6 +483,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"للسماح للتطبيق باستخدام خدمة الرسائل الفورية لإجراء المكالمات بدون تدخل منك."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"قراءة حالة الهاتف والهوية"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"للسماح للتطبيق بالدخول إلى ميزات الهاتف في الجهاز. ويتيح هذا الإذن للتطبيق تحديد رقم الهاتف ومعرّفات الجهاز، وما إذا كانت هناك مكالمة نشطة والرقم البعيد الذي تم الاتصال به في المكالمة."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"قراءة حالة وهوية الاتصال الهاتفي الأساسيتين"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"يسمح هذا الإذن للتطبيق بالوصول إلى ميزات الاتصال الهاتفي الأساسية للجهاز."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"توجيه المكالمات من خلال النظام"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"يسمح للتطبيق بتوجيه المكالمات من خلال النظام لتحسين تجربة الاتصال."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"رؤية المكالمات والتحكّم فيها من خلال النظام"</string>
@@ -634,6 +636,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"انقر لحذف نموذج الوجه ثم أضِف نموذجًا لوجهك مرة أخرى."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"إعداد ميزة \"فتح الجهاز بالتعرف على الوجه\""</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"يمكنك فتح قفل هاتفك بمجرّد النظر إلى الشاشة."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"‏لاستخدام ميزة \"فتح الجهاز بالتعرف على الوجه\"، عليك منح إذن "<b>"الوصول إلى الكاميرا"</b>" في الإعدادات &gt; الخصوصية."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"إعداد المزيد من الطرق لفتح قفل الجهاز"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"انقر لإضافة بصمة إصبع."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"فتح الجهاز ببصمة الإصبع"</string>
@@ -1356,10 +1359,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"جارٍ تحضير <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"بدء التطبيقات."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"جارٍ إعادة التشغيل."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"هل تريد إيقاف الشاشة؟"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"أثناء إعداد بصمة الإصبع، ضغطت على زر التشغيل.\n\nيؤدي هذا الإجراء عادةً إلى إيقاف الشاشة."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"إيقاف"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"إلغاء"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"هل تريد مواصلة عملية الإعداد؟"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ضغطت على زر التشغيل، يؤدي هذا عادةً إلى إيقاف الشاشة.\n\nجرِّب النقر بخفة أثناء إعداد بصمتك."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"إيقاف الشاشة"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"مواصلة عملية الإعداد"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"هل تريد مواصلة تأكيد بصمة إصبعك؟"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ضغطت على زر التشغيل، يؤدي هذا عادةً إلى إيقاف الشاشة.\n\nجرِّب النقر بخفة لتأكيد بصمة إصبعك."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"إيقاف الشاشة"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"متابعة"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> يعمل"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"انقر للعودة إلى اللعبة"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"اختيار اللعبة"</string>
@@ -1727,7 +1734,7 @@
<string name="default_audio_route_category_name" msgid="5241740395748134483">"النظام"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"صوت بلوتوث"</string>
<string name="wireless_display_route_description" msgid="8297563323032966831">"عرض شاشة لاسلكي"</string>
- <string name="media_route_button_content_description" msgid="2299223698196869956">"إرسال"</string>
+ <string name="media_route_button_content_description" msgid="2299223698196869956">"البث"</string>
<string name="media_route_chooser_title" msgid="6646594924991269208">"الاتصال بجهاز"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"بث الشاشة على الجهاز"</string>
<string name="media_route_chooser_searching" msgid="6119673534251329535">"جارٍ البحث عن الأجهزة…"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 4cfb88ccf00e..9dbfb2e81e86 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"আপোনাৰ হস্তক্ষেপৰ অবিহনে আইএমএছ সেৱা ব্যৱহাৰ কৰি কল কৰিবলৈ এপক অনুমতি দিয়ে।"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ফ\'নৰ স্থিতি আৰু পৰিচয় পঢ়ক"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ডিভাইচত থকা ফ\'নৰ সুবিধাসমূহ ব্য়ৱহাৰ কৰিবলৈ এপটোক অনুমতি দিয়ে৷ এই অনুমতিয়ে কোনো কল সক্ৰিয় হৈ থাককেই বা নাথাকক আৰু দূৰবৰ্তী নম্বৰটো কলৰ দ্বাৰা সংযোজিত হওকেই বা নহওক এপটোক ফ\'ন নম্বৰ আৰু ডিভাইচৰ পৰিচয় নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ে৷"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"প্ৰাথমিক টেলিফ\'নী স্থিতি আৰু পৰিচয় পঢ়ক"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"এপ্‌টোক ডিভাইচটোৰ প্ৰাথমিক টেলিফ’নী সুবিধাসমূহ এক্সেছ কৰাৰ অনুমতি দিয়ে।"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ছিষ্টেমৰ জৰিয়তে কল কৰিব পাৰে"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"কল কৰাৰ অভিজ্ঞতাক উন্নত কৰিবলৈ এপটোক ছিষ্টেমৰ জৰিয়তে কলসমূহ কৰিবলৈ দিয়ে।"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ছিষ্টেমৰ জৰিয়তে কলবোৰ চোৱা আৰু নিয়ন্ত্ৰণ কৰা।"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"আপোনাৰ মুখাৱয়বৰ মডেলটো মচিবলৈ টিপক, তাৰ পাছত পুনৰ আপোনাৰ মুখাৱয়ব যোগ দিয়ক"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ফেচ আনলক সুবিধাটো ছেট আপ কৰক"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"আপোনাৰ ফ’নটোলৈ চাই সেইটো আনলক কৰক"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ফেচ আনলক সুবিধাটো ব্যৱহাৰ কৰিবলৈ ছেটিং &gt; গোপনীয়তাত "<b>"কেমেৰাৰ এক্সেছ"</b>" অন কৰক"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"আনলক কৰাৰ অধিক উপায় ছেট আপ কৰক"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"এটা ফিংগাৰপ্ৰিণ্ট যোগ দিবলৈ টিপক"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ফিংগাৰপ্ৰিন্ট আনলক"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>সাজু কৰি থকা হৈছে।"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"আৰম্ভ হৈ থকা এপসমূহ।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"বুট কাৰ্য সমাপ্ত কৰিছে।"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"স্ক্ৰীন অফ কৰিবনে?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"আপোনাৰ ফিংগাৰপ্ৰিণ্ট ছেট আপ কৰাৰ সময়ত, আপুনি পাৱাৰ বুটামটো টিপিছে।\n\nএইটোৱে সচৰাচৰ আপোনাৰ স্ক্ৰীনখন অফ কৰে।"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"অফ কৰক"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"বাতিল কৰক"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ছেট আপ অব্যাহত ৰাখিবনে?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"আপুনি পাৱাৰ বুটামটো টিপিছে — এইটোৱে সাধাৰণতে স্ক্ৰীনখন অফ কৰে।\n\nআপোনাৰ ফিংগাৰপ্ৰিণ্টটো ছেট আপ কৰাৰ সময়ত লাহেকৈ টিপি চাওক।"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"স্ক্ৰীন অফ কৰক"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ছেট আপ অব্যাহত ৰাখক"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ফিংগাৰপ্ৰিণ্ট সত্যাপন কৰা জাৰি ৰাখিবনে?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"আপুনি পাৱাৰ বুটামটো টিপিছে — এইটোৱে সাধাৰণতে স্ক্ৰীনখন অফ কৰে।\n\nআপোনাৰ ফিংগাৰপ্ৰিণ্টটো সত্যাপন কৰিবলৈ লাহেকৈ টিপি চাওক।"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"স্ক্ৰীন অফ কৰক"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"অব্যাহত ৰাখক"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> চলি আছে"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"গেইমলৈ উভতি যাওক"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"গেইম বাছনি কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 136563a3859c..1c9355264d8c 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Tətbiqə müdaxilə olmadan zəng etmək üçün IMS xidmətindən istifadə etməyə imkan verir."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"telefon statusunu və identifikasiyanı oxuyur"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Tətbiqə cihazın telefon funksiyalarına giriş icazəsi verir. Belə icazəli tətbiq bu telefonun nömrəsini və cihaz İD\'ni, zəngin aktiv olub-olmadığını və zəng edilən nömrəni müəyyən edə bilər."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"əsas telefoniya statusu və identifikasiyasını oxuyun"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Tətbiqə cihazın əsas telefoniya funksiyalarına giriş etmək imkanı verir."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"zəngləri sistem üzərindən yönləndirin"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Tətbiqə, zəng təcrübəsini yaxşılaşdırmaq üçün, zəngləri sistem üzərindən yönləndirməyə icazə verilir."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"zənglərə sistemdə baxın və nəzarət edin."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Üz modelinizi silmək üçün toxunun, sonra yenidən üzünüzü əlavə edin"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Üz ilə kiliddən çıxarmanı ayarlayın"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefona baxaraq onu kiliddən çıxarın"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Üz ilə Kiliddən Açma funksiyasını istifadə etmək üçün Ayarlar &gt; Məxfilik bölməsində "<b>"Kameraya girişi"</b>" aktiv edin"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Kiliddən çıxarmağın daha çox yolunu ayarlayın"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Barmaq izi əlavə etmək üçün toxunun"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Barmaq izi ilə kiliddən çıxarma"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> proqramının hazırlanması."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Tətbiqlər başladılır."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Yükləmə başa çatır."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ekran deaktiv edilsin?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Barmaq izinizi ayarlayarkən Qidalanma düyməsinə basdınız.\n\nBu, adətən ekranınızı deaktiv edir."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Deaktiv edin"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Ləğv edin"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Ayarlamağa davam edilsin?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Qidalanma düyməsini basdınız — adətən bu, ekranı söndürür.\n\nBarmaq izini ayarlayarkən yüngülcə toxunmağa çalışın."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Ekranı söndürün"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Ayarlamağa davam edin"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Barmaq izini doğrulamağa davam edilsin?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Qidalanma düyməsini basdınız — adətən bu, ekranı söndürür.\n\nBarmaq izini doğrulamaq üçün yüngülcə toxunmağa çalışın."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ekranı söndürün"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Davam edin"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> çalışır"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Oyuna qayıtmaq üçün klikləyin"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Oyun seçin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 00fc2b5dac5b..92416316cb85 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -474,6 +474,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Dozvoljava aplikaciji da koristi uslugu razmene trenutnih poruka da bi upućivala pozive bez vaše intervencije."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"čitanje statusa i identiteta telefona"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Dozvoljava aplikaciji da pristupa funkcijama telefona na uređaju. Ova dozvola omogućava aplikaciji da utvrdi broj telefona i ID-ove uređaja, zatim da li je poziv aktivan, kao i broj daljinskog uređaja sa kojim je uspostavljen poziv."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"očitavanje osnovnog telefonskog statusa i identiteta"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Omogućava aplikaciji da pristupa osnovnim telefonskim funkcijama uređaja."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"preusmeravanje poziva preko sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Dozvoljava aplikaciji da preusmerava pozive preko sistema da bi poboljšala doživljaj pozivanja."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"pregled i kontrola poziva preko sistema."</string>
@@ -625,6 +627,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da biste izbrisali model lica, pa ponovo dodajte svoje lice"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Podesite otključavanje licem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon tako što ćete ga pogledati"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da biste koristili otključavanje licem, uključite "<b>"pristup kameri"</b>" u odeljku Podešavanja &gt; Privatnost"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Podesite još načina za otključavanje"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da biste dodali otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
@@ -1296,10 +1299,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Priprema se <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Pokretanje aplikacija."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Završavanje pokretanja."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Želite da isključite ekran?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Pritisli ste dugme za uključivanje tokom podešavanja otiska prsta.\n\nTako se najčešće isključuje ekran."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Isključi"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Otkaži"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Želite li da nastavite sa podešavanjem?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnuli ste dugme za uključivanje – time obično isključujete ekran.\n\nProbajte lagano da dodirnete dok podešavate otisak prsta."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Isključi ekran"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Nastavi podešavanje"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Nastavljate verifikaciju otiska prsta?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnuli ste dugme za uključivanje – time obično isključujete ekran.\n\nProbajte lagano da dodirnete da biste verifikovali otisak prsta."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Isključi ekran"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Nastavi"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> je pokrenuta"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Dodirnite da biste se vratili u igru"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Odaberite igru"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 0157e66b5118..849d53b0ad04 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -404,7 +404,7 @@
<string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"Дазваляе прыкладанню захоўваць некаторыя пастаянныя часткi ў памяцi. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых прыкладанняў, i запаволiць працу планшэта."</string>
<string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"Дазваляе праграме пастаянна захоўваць некаторыя свае часткі ў памяці прылады. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых праграм, і запаволіць працу прылады Android TV."</string>
<string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Дазваляе прыкладанню захоўваць некаторыя пастаянныя часткi ў памяцi. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых прыкладанняў, i запаволiць працу тэлефона."</string>
- <string name="permlab_foregroundService" msgid="1768855976818467491">"запусціць асноўныя сэрвісы"</string>
+ <string name="permlab_foregroundService" msgid="1768855976818467491">"запусціць актыўныя сэрвісы"</string>
<string name="permdesc_foregroundService" msgid="8720071450020922795">"Дазваляе праграме выкарыстоўваць асноўныя сэрвісы."</string>
<string name="permlab_getPackageSize" msgid="375391550792886641">"вымерыць прастору для захоўвання прыкладання"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"Дазваляе прыкладанням атрымліваць яго код, дадзеныя і аб\'ём кэш-памяці"</string>
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Дазваляе праграмам выкарыстоўваць службу IMS, каб рабіць выклікі без вашага ўмяшання."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"чытанне статусу тэлефона і ідэнтыфікацыя"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Дазваляе прыкладанням атрымлiваць доступ да функцый тэлефона на прыладзе. Дзякуючы гэтаму дазволу прыкладанне можа вызначаць iдэнтыфiкатары нумару тэлефона i прылады, незалежна ад таго, цi актыўны выклiк, i аддалены нумар, на якi робiцца выклiк."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"счытваць даныя пра асноўны стан тэлефаніі і ідэнтыфікацыю"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Дазваляе праграме мець доступ да асноўных функцый тэлефаніі на прыладзе."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"перанакіраванне выклікаў праз сістэму"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Дазваляе праграме перанакіроўваць выклікі праз сістэму ў мэтах паляпшэння выклікаў."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"праглядаць выклікі і кіраваць імі праз сістэму."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Націсніце, каб выдаліць мадэль твару, пасля дадайце твар яшчэ раз"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Наладзьце распазнаванне твару"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Разблакіруйце свой тэлефон, паглядзеўшы на яго"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Каб выкарыстоўваць распазнаванне твару, уключыце "<b>"доступ да камеры"</b>" праз раздзел \"Налады &gt; Прыватнасць\""</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Наладзьце дадатковыя спосабы разблакіроўкі"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Націсніце, каб дадаць адбітак пальца"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Разблакіраванне адбіткам пальца"</string>
@@ -726,8 +729,8 @@
<string name="permdesc_handoverStatus" msgid="3842269451732571070">"Дазваляе праграме атрымліваць інфармацыю аб бягучых перадачах Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="710576248717404416">"выдаленне сертыфікатаў DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Дазваляе праграме выдаляць сертыфікаты DRM. Ніколі не павінна патрабавацца для звычайных праграм."</string>
- <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"прывязка да службы паведамленняў аператара"</string>
- <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"Дазваляе ўладальніку выконваць прывязку да інтэрфейсу верхняга ўзроўню службы паведамленняў аператара. Ніколі не павінна патрабавацца для звычайных праграм."</string>
+ <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"падключэнне да сэрвісу абмену паведамленнямі аператара"</string>
+ <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"Дазваляе ўладальніку выконваць падключэнне да базавага інтэрфейсу сэрвісу абмену паведамленнямі аператара. Звычайныя праграмы ніколі не выкарыстоўваюць гэты дазвол."</string>
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"прывязвацца з сэрвісаў аператара"</string>
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"Дазваляе ўладальніку ажыццяўляць прывязку да сэрвісаў аператара. Ніколі не павінна патрабавацца для звычайных праграм."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"атрымліваць доступ да рэжыму «Не турбаваць»"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Падрыхтоўка <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск прыкладанняў."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Завяршэнне загрузкі."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Выключыць экран?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Падчас наладкі адбітка пальца вы націскалі кнопку сілкавання.\n\nЗвычайна гэта дзеянне выключае экран."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Выключыць"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Скасаваць"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Працягнуць наладжванне?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Вы націснулі кнопку сілкавання. Звычайна ў выніку гэтага дзеяння выключаецца экран.\n\nПадчас наладжвання адбітка пальца злёгку дакраніцеся да кнопкі."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Выключыць экран"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Працягнуць наладку"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Працягнуць спраўджанне адбітка пальца?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Вы націснулі кнопку сілкавання. Звычайна ў выніку гэтага дзеяння выключаецца экран.\n\nКаб спраўдзіць адбітак пальца, злёгку дакраніцеся да кнопкі."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Выключыць экран"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Працягнуць"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Прыкладанне \"<xliff:g id="APP">%1$s</xliff:g>\" запушчанае"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Націсніце, каб вярнуцца да гульні"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Выберыце гульню"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index d75d2e7543d6..dad2f03cbd68 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Разрешава на приложението да използва услугата за незабавни съобщения за извършване на обаждания без намеса от ваша страна."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"четене на състоянието и идентификационните данни на телефона"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Разрешава на приложението достъп до телефонните функции на устройството. Това разрешение позволява на приложението да определя телефонния номер и идентификационния номер на устройството, дали се води разговор и отдалечения номер, до който е установена връзка с обаждането."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"четене на основното телефонно състояние и идентичност"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Разрешава на приложението да осъществява достъп до основните телефонни функции на устройството."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"маршрутизиране на обажданията чрез системата"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Разрешава на приложението да маршрутизира обажданията си чрез системата с цел подобряване на свързаната с тях практическа работа."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"вижда и управлява обажданията чрез системата."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Докоснете, за да изтриете модела на лицето си, след което добавете лицето си отново"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Настройване на отключването с лице"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Отключвайте телефона си, като го погледнете"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"За да използвате функцията „Отключване с лице“, включете "<b>"достъпа до камерата"</b>" от „Настройки &gt; Поверителност“"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Настройване на още начини за отключване на телефона"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Докоснете, за да добавите отпечатък"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Отключване с отпечатък"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> се подготвя."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Приложенията се стартират."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Зареждането завършва."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Да се изключи ли екранът?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"При настройването на отпечатъка си натиснахте бутона за захранване.\n\nТова действие обикновено изключва екрана."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Изключване"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Отказ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Искате ли да продължите с настройването?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Натиснахте бутона за включване/изключване – това обикновено изключва екрана.\n\nОпитайте да докоснете леко, докато настройвате отпечатъка си."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Изключване на екрана"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Напред с настройв."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Напред с потвърждаването на отпечатъка?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Натиснахте бутона за включване/изключване – това обикновено изключва екрана.\n\nОпитайте да докоснете леко, за да потвърдите отпечатъка си."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Изключване на екрана"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Напред"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> се изпълнява"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Докоснете, за да се върнете към играта"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Избиране на игра"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 30809483882d..d560d5c1e107 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"আপনার হস্তক্ষেপ ছাড়াই কল করতে অ্যাপ্লিকেশানটিকে IMS পরিষেবা ব্যবহারের অনুমতি দিন৷"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ফোনের স্থিতি এবং পরিচয় পড়ুন"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"অ্যাপ্লিকেশানটিকে ডিভাইসের ফোন বৈশিষ্ট্যগুলিকে অ্যাক্সেস করার অনুমতি দেয়৷ এই অনুমতিটি অ্যাপ্লিকেশানটিকে একটি কল সক্রিয় থাকা অবস্থায় এবং দূরবর্তী নম্বর একটি কল দ্বারা সংযুক্ত থাকাকালীনও ফোন নম্বর এবং ডিভাইসের IDগুলি নির্ধারণ করার অনুমতি দেয়৷"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"টেলিফোন সংক্রান্ত স্ট্যাটাস ও পরিচয় সংক্রান্ত প্রাথমিক বিষয় পড়ুন"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"অ্যাপটিকে ডিভাইসের টেলিফোন সংক্রান্ত প্রাথমিক ফিচারগুলি অ্যাক্সেস করার অনুমতি দিন।"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"সিস্টেমের মাধ্যমে কলগুলি রুট করতে দিন"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"কল করার অভিজ্ঞতা উন্নত করার জন্য অ্যাপকে সিস্টেমের মাধ্যমে তার কলগুলি রুট করতে দেয়।"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"সিস্টেমের মাধ্যমে কল দেখা এবং নিয়ন্ত্রণ করা।"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"আপনার ফেস মডেল মুছে দেওয়ার জন্য ট্যাপ করুন এবং তারপরে আবার ফেস যোগ করুন"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"\'ফেস আনলক\' সেট আপ করুন"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"আপনার ফোনের দিকে তাকিয়ে এটিকে আনলক করুন"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"\'ফেস আনলক\' ফিচার ব্যবহার করতে \'সেটিংস ও গোপনীয়তা\' বিকল্পে গিয়ে "<b>"ক্যামেরায় অ্যাক্সেস দিন"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"আনলক করার জন্য বিভিন্ন উপায়ে সেট আপ করুন"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"একটি আঙ্গুলের ছাপ যোগ করতে ট্যাপ করুন"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ফিঙ্গারপ্রিন্ট আনলক"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> প্রস্তুত করা হচ্ছে৷"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"অ্যাপ্লিকেশানগুলি শুরু করা হচ্ছে৷"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"চালু করা সম্পূর্ণ হচ্ছে৷"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"স্ক্রিন বন্ধ করবেন?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"আপনার আঙ্গুলের ছাপ সেট আপ করার সময়, পাওয়ার বোতাম প্রেস করেছিলেন।\n\nএর ফলে সাধারণত আপনার স্ক্রিন বন্ধ হয়ে যায়।"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"বন্ধ করুন"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"বাতিল করুন"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"সেট-আপ করা চালিয়ে যাবেন?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"আপনি \'পাওয়ার\' বোতাম প্রেস করেছেন — এর ফলে সাধারণত স্ক্রিন বন্ধ হয়ে যায়।\n\nআঙ্গুলের ছাপ সেট-আপ করার সময় হালকাভাবে ট্যাপ করে দেখুন।"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"স্ক্রিন বন্ধ করুন"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"সেট-আপ চালিয়ে যান"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"আঙ্গুলের ছাপ যাচাই করা চালিয়ে যাবেন?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"আপনি \'পাওয়ার\' বোতাম প্রেস করেছেন — এর ফলে সাধারণত স্ক্রিন বন্ধ হয়ে যায়।\n\nআঙ্গুলের ছাপ যাচাই করতে হালকাভাবে ট্যাপ করে দেখুন।"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"স্ক্রিন বন্ধ করুন"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"এগিয়ে যান"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> চলছে"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"গেমে ফিরে আসতে ট্যাপ করুন"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"গেম বেছে নিন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index ea374900ac35..68ccf1546bfc 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -474,6 +474,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Omogućava aplikaciji da koristi IMS uslugu za pozivanje bez vaše intervencije."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"čitanje statusa i identiteta telefona"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Omogućava aplikaciji pristup telefonskim funkcijama uređaja. Ovo odobrenje omogućava aplikaciji određivanje telefonskog i identifikacionog broja uređaja, bez obzira da li je poziv aktivan i da li je uspostavljena veza sa pozivanim brojem."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"očitavanje osnovnog telefonskog statusa i identiteta"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Dozvoljava aplikaciji pristup osnovnim telefonskim funkcijama uređaja."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"usmjeravanje poziva preko sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Dopušta aplikaciji da pozive usmjeri preko sistema radi poboljšanja iskustva pozivanja."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"vidjeti i kontrolirati pozive preko sistema."</string>
@@ -625,6 +627,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da izbrišete model lica, a zatim ponovo dodajte lice"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Postavite otključavanje licem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon gledajući u njega"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da koristite otključavanje licem, uključite "<b>"Pristup kameri"</b>" u meniju Postavke &gt; Privatnost"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Postavite više načina otključavanja"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da dodate otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
@@ -1296,10 +1299,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Pripremanje aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Pokretanje aplikacija."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Pokretanje pri kraju."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Isključiti ekran?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Prilikom postavljanja otiska prsta, pritisnuli ste dugme za uključivanje.\n\nTime se obično isključuje ekran."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Isključi"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Otkaži"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Nastaviti postavljanje?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnuli ste dugme za uključivanje. Tako se obično isključuje ekran.\n\nPokušajte ga lagano dodirnuti dok postavljate otisak prsta."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Isključi ekran"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Nastavi postavljanje"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Nastaviti s potvrđivanjem otiska prsta?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnuli ste dugme za uključivanje. Tako se obično isključuje ekran.\n\nPokušajte ga lagano dodirnuti da potvrdite otisak prsta."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Isključi ekran"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Nastavi"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Pokrenuta je aplikacija <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Dodirnite za povratak u igru"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Odaberite igru"</string>
@@ -2049,7 +2056,7 @@
<string name="demo_restarting_message" msgid="1160053183701746766">"Vraćanje uređaja na početne postavke…"</string>
<string name="suspended_widget_accessibility" msgid="6331451091851326101">"Onemogućen <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="conference_call" msgid="5731633152336490471">"Konferencijski poziv"</string>
- <string name="tooltip_popup_title" msgid="7863719020269945722">"Savjet za alat"</string>
+ <string name="tooltip_popup_title" msgid="7863719020269945722">"Skočni opis"</string>
<string name="app_category_game" msgid="4534216074910244790">"Igre"</string>
<string name="app_category_audio" msgid="8296029904794676222">"Muzika i zvuk"</string>
<string name="app_category_video" msgid="2590183854839565814">"Filmovi i videozapisi"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 91ba2f0095e7..726bb3f59a1a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permet que l\'aplicació utilitzi el servei IMS per fer trucades sense la teva intervenció."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"veure l\'estat i la identitat del telèfon"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permet que l\'aplicació accedeixi a les funcions de telèfon del dispositiu. Aquest permís permet que l\'aplicació determini el número de telèfon i els identificadors del dispositiu, si hi ha una trucada activa i el número remot connectat amb una trucada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"llegir la identitat i l\'estat bàsics de telefonia"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permet que l\'aplicació accedeixi a les funcions de telefonia bàsiques del dispositiu."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"encaminar trucades a través del sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permet que l\'aplicació encamini les trucades a través del sistema per millorar-ne l\'experiència."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"consulta i controla les trucades a través del sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toca per suprimir el teu model facial i, a continuació, torna a afegir la teva cara"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueig facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Mira el telèfon per desbloquejar-lo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Per utilitzar Desbloqueig facial, activa "<b>"Accés a la càmera"</b>" a Configuració &gt; Privadesa"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura més maneres de desbloquejar el dispositiu"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca per afegir una empremta digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueig amb empremta digital"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"S\'està preparant <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"S\'estan iniciant les aplicacions."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"S\'està finalitzant l\'actualització."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vols apagar la pantalla?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Mentre configuraves la teva empremta digital has premut el botó d\'engegada.\n\nAixò normalment apaga la pantalla."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Desactiva"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancel·la"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Vols continuar amb la configuració?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Has premut el botó d\'engegada, fet que sol apagar la pantalla.\n\nProva de tocar-lo lleugerament en configurar l\'empremta digital."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Apaga la pantalla"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continua configurant"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vols continuar verificant l\'empremta?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Has premut el botó d\'engegada, fet que sol apagar la pantalla.\n\nProva de tocar-lo lleugerament per verificar l\'empremta digital."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Apaga la pantalla"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continua"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> s\'està executant"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Toca per tornar al joc"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Tria el joc"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index c928655ee2b7..abbcc2c45fe8 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Umožňuje aplikaci používat službu zasílání rychlých zpráv k uskutečňování hovorů bez vašeho zásahu."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"čtení stavu a identity telefonu"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Umožňuje aplikaci získat přístup k telefonním funkcím zařízení. Toto oprávnění umožňuje aplikaci zjistit telefonní číslo telefonu, identifikační čísla zařízení, zda zrovna probíhá hovor, a vzdálené číslo, ke kterému je hovor připojen."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"čtení základního stavu a identity související s telefonováním"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Umožňuje aplikaci přístup k základním telefonickým funkcím zařízení."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"směrování volání prostřednictvím systému"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Umožňuje aplikaci směrovat volání prostřednictvím systému za účelem vylepšení funkcí volání."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"zobrazení a ovládání hovorů v systému."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Klepnutím svůj model obličeje smažte a potom ho přidejte znovu"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavte odemknutí obličejem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefon odemknete pohledem"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pokud chcete používat odemknutí obličejem, v Nastavení &gt; Soukromí zapnetě "<b>"přístup k fotoaparátu"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavte si více způsobů odemykání"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Klepnutím přidáte otisk prstu"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odemknutí otiskem prstu"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Příprava aplikace <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Spouštění aplikací."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Dokončování inicializace."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vypnout obrazovku?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Při nastavování otisku prstu jste stiskli vypínač.\n\nTen obvykle vypne obrazovku."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Vypnout"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Zrušit"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Pokračovat v nastavování?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Stiskli jste vypínač – tím se obvykle vypíná obrazovka.\n\nPři nastavování otisku prstu je třeba klepat lehce."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Vypnout obrazovku"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Pokračovat v nastavení"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Pokračovat v ověřování otisku prstu?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Stiskli jste vypínač – tím se obvykle vypíná obrazovka.\n\nZkuste lehkým klepnutím ověřit svůj otisk prstu."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Vypnout obrazovku"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Pokračovat"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Běží aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Klepnutím se vrátíte do hry"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Vyberte hru"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 4c60718557db..57673f44cdcc 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Tillader, at appen kan bruge chat-tjenesten til at foretage opkald, uden du gør noget."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"læse telefonens status og identitet"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Tillader, at appen kan få adgang til telefonfunktionerne på enheden. Med denne tilladelse kan appen fastslå telefonnummeret og enheds-id\'erne, hvorvidt et opkald er aktivt samt det eksterne nummer, der oprettes forbindelse til via et opkald."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"tilgå grundlæggende oplysninger om telefonistatus og -identitet"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Giver appen adgang til enhedens grundlæggende telefonifunktioner."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"dirigere opkald gennem systemet"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Tillader appen at dirigere sine opkald gennem systemet for at forbedre opkaldsoplevelsen."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"se og styre opkald via systemet."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tryk for at slette din ansigtsmodel, og tilføj derefter dit ansigt igen"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurer ansigtslås"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Lås din telefon op ved at kigge på den"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Hvis du vil bruge ansigtslåsen, skal du aktivere "<b>"Kameraadgang"</b>" under Indstillinger &gt; Privatliv"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurer flere måder at låse op på"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tryk for at tilføje et fingeraftryk"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Oplåsning med fingeraftryk"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Forbereder <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Åbner dine apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Gennemfører start."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vil du slukke skærmen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Ved konfigurationen af dit fingeraftryk trykkede du på afbryderknappen.\n\nDet medfører normalt, at din skærm slukkes."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Sluk"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Annuller"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Vil du fortsætte konfigurationen?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du har trykket på afbryderknappen, hvilket som regel slukker skærmen.\n\nPrøv at trykke let på knappen, mens du konfigurerer dit fingeraftryk."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Sluk skærm"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Fortsæt"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vil du bekræfte dit fingeraftryk?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du har trykket på afbryderknappen, hvilket som regel slukker skærmen.\n\nPrøv at trykke let på knappen for at bekræfte dit fingeraftryk."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Sluk skærm"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Fortsæt"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> er i gang"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tryk for at vende tilbage til spillet"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Vælg et spil"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 25ba98b60b43..fde9b0c63539 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Ermöglicht der App die Verwendung des IMS-Dienstes zum Tätigen von Anrufen ohne Nutzereingriffe"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"Telefonstatus und Identität abrufen"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Ermöglicht der App, auf die Telefonfunktionen des Geräts zuzugreifen. Die Berechtigung erlaubt der App, die Telefonnummer und Geräte-IDs zu erfassen, festzustellen, ob gerade ein Gespräch geführt wird, und die Rufnummer verbundener Anrufer zu lesen."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"Grundlegenden Telefoniestatus und Identität lesen"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Ermöglicht der App, auf die grundlegenden Telefoniefunktionen des Geräts zuzugreifen."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"Anrufe über das System durchführen"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Ermöglicht der App, Anrufe über das System durchzuführen, um die Anrufqualität zu verbessern."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Anrufe durch das System einsehen und verwalten."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tippe, um dein Gesichtsmodell zu löschen, und füge es dann noch einmal hinzu"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Entsperrung per Gesichtserkennung einrichten"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Entsperre dein Smartphone, indem du es ansiehst"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Wenn du die Entsperrung per Gesichtserkennung verwenden möchtest, aktiviere in den Einstellungen unter „Datenschutz“ die Option "<b>"Kamerazugriff"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Weitere Möglichkeiten zum Entsperren einrichten"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tippe, um einen Fingerabdruck hinzuzufügen"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Entsperrung per Fingerabdruck"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> wird vorbereitet"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Apps werden gestartet..."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Start wird abgeschlossen..."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Display ausschalten?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Beim Einrichten deines Fingerabdrucks hast du die Ein-/Aus-Taste gedrückt.\n\nDamit wird üblicherweise das Display ausgeschaltet."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Ausschalten"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Abbrechen"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Einrichtung fortsetzen?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du hast die Ein-/Aus-Taste gedrückt — damit wird der Bildschirm ausgeschaltet.\n\nTippe die Taste leicht an, um deinen Fingerabdruck einzurichten."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Ausschalten"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Weiter einrichten"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Mit der Fingerabdruckprüfung fortfahren?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du hast die Ein-/Aus-Taste gedrückt — damit wird der Bildschirm ausgeschaltet.\n\nTippe die Taste leicht an, um mit deinem Fingerabdruck deine Identität zu bestätigen."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ausschalten"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Fortfahren"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> läuft"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tippe, um zum Spiel zurückzukehren"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Spiel wählen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 4764e6a4d63f..b0ad4da8246c 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Επιτρέπει στην εφαρμογή τη χρήση της υπηρεσίας IMS για την πραγματοποίηση κλήσεων χωρίς τη δική σας παρέμβαση."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"διαβάζει την κατάσταση και ταυτότητα τηλεφώνου"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Επιτρέπει στην εφαρμογή την πρόσβαση στις λειτουργίες τηλεφώνου της συσκευής. Αυτή η άδεια δίνει τη δυνατότητα στην εφαρμογή να καθορίζει τον αριθμό τηλεφώνου και τα αναγνωριστικά συσκευών, εάν μια κλήση είναι ενεργή, καθώς και τον απομακρυσμένο αριθμό που συνδέεται από μια κλήση."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ανάγνωση βασικών πληροφοριών κατάστασης και ταυτότητας τηλεφωνίας"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Επιτρέπει στην εφαρμογή να έχει πρόσβαση στις βασικές λειτουργίες τηλεφωνίας της συσκευής."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"δρομολόγηση κλήσεων μέσω του συστήματος"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Επιτρέπει στην εφαρμογή να δρομολογεί τις κλήσεις της μέσω του συστήματος για να βελτιώσει την εμπειρία κλήσης."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"προβολή και έλεγχος κλήσεων μέσω του συστήματος."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Πατήστε για να διαγράψετε το μοντέλο προσώπου και, στη συνέχεια, προσθέστε το πρόσωπό σας ξανά."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Ρύθμιση της λειτουργίας Ξεκλείδωμα με το πρόσωπο"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ξεκλειδώστε το τηλέφωνό σας απλώς κοιτώντας το"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Για να χρησιμοποιήσετε τη λειτουργία Ξεκλείδωμα με το πρόσωπο, ενεργοποιήστε την επιλογή "<b>"Πρόσβαση στην κάμερα"</b>" από το μενού Ρυθμίσεις &gt; Απόρρητο"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Ρυθμίστε περισσότερους τρόπους ξεκλειδώματος"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Πατήστε για να προσθέσετε δακτυλικό αποτύπωμα"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Ξεκλείδωμα με δακτυλικό αποτύπωμα"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Προετοιμασία <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Έναρξη εφαρμογών."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Ολοκλήρωση εκκίνησης."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Απενεργοποίηση οθόνης;"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Κατά τη ρύθμιση του δακτυλικού σας αποτυπώματος, πατήσατε το κουμπί λειτουργίας.\n\nΑυτό απενεργοποιεί συνήθως την οθόνη."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Απενεργοποίηση"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Ακύρωση"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Συνέχιση ρύθμισης;"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Πατήσατε το κουμπί λειτουργίας. Αυτό συνήθως απενεργοποιεί την οθόνη.\n\nΔοκιμάστε να πατήσετε απαλά κατά τη ρύθμιση του δακτυλικού σας αποτυπώματος."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Απενεργοπ. οθόνης"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Συνέχιση ρύθμισης"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Συνέχιση επαλήθευσης δακτ. αποτυπώματος;"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Πατήσατε το κουμπί λειτουργίας. Αυτό συνήθως απενεργοποιεί την οθόνη.\n\nΔοκιμάστε να πατήστε απαλά για να επαληθεύσετε το δακτυλικό σας αποτύπωμα."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Απενεργοπ. οθόνης"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Συνέχεια"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Η εφαρμογή <xliff:g id="APP">%1$s</xliff:g> εκτελείται"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Πατήστε για να επιστρέψετε στο παιχνίδι"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Επιλέξτε παιχνίδι"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index a7647b6019e7..fd68ddd1549a 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Allows the app to use the IMS service to make calls without your intervention."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"read phone status and identity"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"read basic telephony status and identity"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Allows the app to access the basic telephony features of the device."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"see and control calls through the system."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starting apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finishing boot."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Turn off screen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"While setting up your fingerprint, you pressed the power button.\n\nThis usually turns off your screen."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Turn off"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancel"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continue setup?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Turn off screen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continue setup"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continue verifying your fingerprint?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Turn off screen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continue"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tap to return to game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choose game"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 8c28433e9d9d..265d2e673049 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Allows the app to use the IMS service to make calls without your intervention."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"read phone status and identity"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"read basic telephony status and identity"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Allows the app to access the basic telephony features of the device."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"see and control calls through the system."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starting apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finishing boot."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Turn off screen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"While setting up your fingerprint, you pressed the power button.\n\nThis usually turns off your screen."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Turn off"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancel"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continue setup?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Turn off screen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continue setup"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continue verifying your fingerprint?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Turn off screen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continue"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tap to return to game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choose game"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 6e2595462ecb..8ee21a3abd43 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Allows the app to use the IMS service to make calls without your intervention."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"read phone status and identity"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"read basic telephony status and identity"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Allows the app to access the basic telephony features of the device."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"see and control calls through the system."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starting apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finishing boot."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Turn off screen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"While setting up your fingerprint, you pressed the power button.\n\nThis usually turns off your screen."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Turn off"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancel"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continue setup?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Turn off screen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continue setup"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continue verifying your fingerprint?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Turn off screen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continue"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tap to return to game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choose game"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 53031c69ca57..7fada24faaae 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Allows the app to use the IMS service to make calls without your intervention."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"read phone status and identity"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"read basic telephony status and identity"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Allows the app to access the basic telephony features of the device."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"see and control calls through the system."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starting apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finishing boot."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Turn off screen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"While setting up your fingerprint, you pressed the power button.\n\nThis usually turns off your screen."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Turn off"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancel"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continue setup?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Turn off screen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continue setup"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continue verifying your fingerprint?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Turn off screen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continue"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tap to return to game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choose game"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 0f0e35a1b550..0eaf8ef7c138 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‏‎‏‎‎Allows the app to use the IMS service to make calls without your intervention.‎‏‎‎‏‎"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎read phone status and identity‎‏‎‎‏‎"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active, and the remote number connected by a call.‎‏‎‎‏‎"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎read basic telephony status and identity‎‏‎‎‏‎"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‎‎‎Allows the app to access the basic telephony features of the device.‎‏‎‎‏‎"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‎route calls through the system‎‏‎‎‏‎"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‏‏‎‏‏‏‎‎Allows the app to route its calls through the system in order to improve the calling experience.‎‏‎‎‏‎"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‎see and control calls through the system.‎‏‎‎‏‎"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‎Tap to delete your face model, then add your face again‎‏‎‎‏‎"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎Set up Face Unlock‎‏‎‎‏‎"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎Unlock your phone by looking at it‎‏‎‎‏‎"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‎‎‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‏‏‎‎‎To use Face Unlock, turn on ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎Camera access‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎ in Settings &gt; Privacy‎‏‎‎‏‎"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‎‏‏‏‎Set up more ways to unlock‎‏‎‎‏‎"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‎‎Tap to add a fingerprint‎‏‎‎‏‎"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‎‎Fingerprint Unlock‎‏‎‎‏‎"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‏‎‎‎Preparing ‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎Starting apps.‎‏‎‎‏‎"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎Finishing boot.‎‏‎‎‏‎"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‎‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‎Turn off screen?‎‏‎‎‏‎"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎While setting up your fingerprint, you pressed the Power button.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎This usually turns off your screen.‎‏‎‎‏‎"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎Turn off‎‏‎‎‏‎"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎Cancel‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎Continue setup?‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎‎‎You pressed the power button — this usually turns off the screen.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try tapping lightly while setting up your fingerprint.‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎Turn off screen‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‏‏‎‎‏‎‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎Continue setup‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‏‎‎‎‏‏‎Continue verifying your fingerprint?‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎You pressed the power button — this usually turns off the screen.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try tapping lightly to verify your fingerprint.‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎Turn off screen‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‎‎Continue‎‏‎‎‏‎"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ running‎‏‎‎‏‎"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‎‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‎Tap to return to game‎‏‎‎‏‎"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎Choose game‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index e7110c9b7e58..93a316ffae83 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -329,17 +329,17 @@
<string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificaciones"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"mostrar notificaciones"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperar el contenido de las ventanas"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecciona el contenido de la ventana con la que estés interactuando."</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspeccionará el contenido de la ventana con la que estés interactuando."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activar la Exploración táctil"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Los elementos que presiones se dirán en voz alta y podrás explorar la pantalla mediante gestos."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Observar el texto que escribes"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Incluye datos personales, como números de tarjeta de crédito y contraseñas."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Controlar la ampliación de pantalla"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controla el posicionamiento y el nivel de zoom de la pantalla."</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controlará el posicionamiento y el nivel de zoom de la pantalla."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Usar gestos"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Permite presionar, deslizar, pellizcar y usar otros gestos."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestos del sensor de huellas dactilares"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Captura los gestos que se hacen en el sensor de huellas dactilares del dispositivo."</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Capturará los gestos que se hacen en el sensor de huellas dactilares del dispositivo."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Tomar captura de pantalla"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Puede tomar una captura de la pantalla."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"desactivar o modificar la barra de estado"</string>
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite que la aplicación utilice el servicio IMS para hacer llamadas sin tu intervención."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"leer la identidad y el estado del dispositivo"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite que la aplicación acceda a las funciones de teléfono del dispositivo. La aplicación puede utilizar este permiso para descubrir identificadores de dispositivos y números de teléfono, para saber si una llamada está activa y para conocer el número remoto con el que se ha establecido conexión mediante una llamada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"leer identidad y estado de telefonía básica"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite que la app acceda a las funciones de telefonía básica del dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"Transmite llamadas a través del sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite que la app transmita las llamadas a través del sistema para mejorar la experiencia de llamadas."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Mirar y controlar las llamadas con el sistema"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Presiona para borrar el modelo de rostro y, luego, vuelve a agregar tu rostro"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloquea el teléfono con solo mirarlo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar Desbloqueo facial, activa el "<b>"Acceso a la cámara"</b>" en Configuración y privacidad"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura más formas de desbloquear el dispositivo"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Presiona para agregar una huella dactilar"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo con huellas dactilares"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando aplicaciones"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalizando el inicio"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"¿Quieres apagar la pantalla?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Al configurar tu huella dactilar, presionaste el botón de encendido.\n\nPor lo general, esta acción apaga la pantalla."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Apagar"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancelar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"¿Continuar con la configuración?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Presionaste el botón de encendido. Por lo general, esta acción apaga la pantalla.\n\nPresiona suavemente mientras configuras tu huella dactilar."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Apagar pantalla"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continuar config."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"¿Verificar huella dactilar?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Presionaste el botón de encendido. Por lo general, esta acción apaga la pantalla.\n\nPresiona suavemente para verificar tu huella dactilar."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Apagar pantalla"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuar"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> en ejecución"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Presiona para volver al juego"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Elige un juego"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index a582a0991e9a..22de5d10b7f0 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite que la aplicación utilice el servicio IMS para realizar llamadas sin tu intervención."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"consultar la identidad y el estado del teléfono"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite que la aplicación acceda a las funciones de teléfono del dispositivo. La aplicación puede utilizar este permiso para descubrir identificadores de dispositivos y números de teléfono, para saber si una llamada está activa y para conocer el número remoto con el que se ha establecido conexión mediante una llamada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"leer el estado y la identidad básicos de telefonía"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite que la aplicación acceda a las funciones de telefonía básicas del dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"direccionar llamadas a través del sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite a la aplicación direccionar sus llamadas hacia el sistema para mejorar la calidad de estas."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ver y controlar llamadas a través del sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toca para eliminar tu modelo facial y luego añade de nuevo tu cara"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloquea el teléfono con solo mirarlo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar Desbloqueo Facial, habilita el "<b>"acceso a la cámara"</b>" en Ajustes y privacidad"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura más formas de desbloqueo"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca para añadir una huella digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo con huella digital"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando aplicaciones"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalizando inicio..."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"¿Apagar pantalla?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Has pulsado el botón de encendido mientras configurabas tu huella digital.\n\nAl hacer esto, se suele apagar la pantalla."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Desactivar"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancelar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"¿Quieres seguir con la configuración?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Has pulsado el botón de encendido, lo que suele apagar la pantalla.\n\nPrueba a apoyar el dedo ligeramente para verificar tu huella digital."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Apagar pantalla"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Seguir configurando"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"¿Seguir verificando tu huella digital?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Has pulsado el botón de encendido, lo que suele apagar la pantalla.\n\nPrueba a apoyar el dedo ligeramente para verificar tu huella digital."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Apagar pantalla"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuar"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> en ejecución"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Toca para volver al juego"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Elegir juego"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 84912d898acd..334a666ffc10 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Võimaldab rakendusel kasutada IMS-teenust kõnede tegemiseks ilma, et peaksite sekkuma."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"Telefoni oleku ja identiteedi lugemine"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Annab rakendusele juurdepääsu seadme telefonifunktsioonidele. See luba võimaldab rakendusel määrata telefoninumbri ja seadme ID-d ning kontrollida, kas kõne on aktiivne ja kaugnumber on kõne kaudu telefoniga ühendatud."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"Lugeda peamiste telefonifunktsioonide olekut ja kasutajate identiteeti"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Võimaldab rakendusel pääseda juurde seadme peamistele telefonifunktsioonidele."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"kõnede marsruutimine süsteemi kaudu"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Võimaldab rakendusel kõnesid süsteemi kaudu marsruutida, et helistamiskogemust täiustada."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"süsteemi kaudu kõnede vaatamine ja juhtimine."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Puudutage näomudeli kustutamiseks, seejärel lisage oma nägu uuesti"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Näoga avamise seadistamine"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Avage telefon seda vaadates"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Näoga avamise kasutamiseks lülitage menüüs Seaded &gt; Privaatsus sisse "<b>"juurdepääs kaamerale"</b>"."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Seadistage rohkem viise avamiseks"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Puudutage sõrmejälje lisamiseks"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Sõrmejäljega avamine"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> ettevalmistamine."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Rakenduste käivitamine."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Käivitamise lõpuleviimine."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Kas lülitada ekraan välja?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Sõrmejälje seadistamisel vajutasite toitenuppu.\n\nSee lülitab ekraanikuva tavaliselt välja."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Lülita välja"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Tühista"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Kas jätkata seadistamist?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Vajutasite toitenuppu – tavaliselt lülitab see ekraani välja.\n\nPuudutage õrnalt ja seadistage oma sõrmejälg."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Lülita ekraan välja"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Jätka seadistamist"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Kas jätkata sõrmejälje kinnitamist?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Vajutasite toitenuppu – tavaliselt lülitab see ekraani välja.\n\nPuudutage õrnalt, et oma sõrmejälg kinnitada."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Lülita ekraan välja"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Jätka"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> töötab"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Puudutage mängu naasmiseks"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Valige mäng"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index d84bdddd907a..df16b7362996 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Zuk ezer egin beharrik gabe deiak egiteko IMS zerbitzua erabiltzeko baimena ematen die aplikazioei."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"irakurri telefonoaren egoera eta identitatea"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Gailuaren telefono-eginbideak atzitzeko baimena ematen die aplikazioei. Baimen horrek aplikazioari telefono-zenbakia eta gailu IDak zein diren, deirik aktibo dagoen eta deia zer zenbakirekin konektatuta dagoen zehazteko baimena ematen die aplikazioei."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"irakurri oinarrizko egoera telefonikoa eta identitatea"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gailuaren oinarrizko eginbide telefonikoak atzitzeko baimena ematen dio aplikazioari."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"bideratu deiak sistemaren bidez"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Deiak sistemaren bidez bideratzea baimentzen die aplikazioei, deien zerbitzua ahal bezain ona izan dadin."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ikusi eta kontrolatu deiak sistemaren bidez."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Sakatu hau aurpegi-eredua ezabatzeko eta, gero, gehitu aurpegia berriro"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfiguratu aurpegi bidez desblokeatzeko eginbidea"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefonoa desblokeatzeko, begira iezaiozu"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Aurpegi bidez desblokeatzeko aukera erabiltzeko, aktibatu "<b>"kamera atzitzeko baimena"</b>" Ezarpenak &gt; Pribatutasuna atalean"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfiguratu telefonoa desblokeatzeko modu gehiago"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Sakatu hau hatz-marka bat gehitzeko"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Hatz-marka bidez desblokeatzea"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> prestatzen."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Aplikazioak abiarazten."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Bertsio-berritzea amaitzen."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Pantaila itzali nahi duzu?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Hatz-marka konfiguratzean, etengailua sakatu duzu.\n\nNormalean, horren ondorioz pantaila itzali egiten da."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Desaktibatu"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Utzi"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Konfiguratzen jarraitu nahi duzu?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Etengailua sakatu duzu; pantaila itzaltzeko balio du horrek.\n\nUki ezazu arin, hatz-marka konfiguratu bitartean."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Itzali pantaila"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Jarraitu konfiguratzen"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Hatz-marka egiaztatzen jarraitu nahi duzu?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Etengailua sakatu duzu; pantaila itzaltzeko balio du horrek.\n\nUki ezazu arin, hatz-marka egiaztatzeko."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Itzali pantaila"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Egin aurrera"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> abian da"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Sakatu jokora itzultzeko"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Aukeratu joko bat"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 8f1daa217325..e66c7837b408 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"‏به برنامه اجازه می‌دهد از سرویس IMS برای برقراری تماس‌ها بدون دخالت شما استفاده کند."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"خواندن وضعیت تلفن و شناسه"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"به برنامه اجازه می‌دهد به ویژگی‌های تلفن دستگاه شما دسترسی پیدا کند. این مجوز به برنامه اجازه می‌دهد شماره تلفن و شناسه‌های دستگاه، فعال بودن یک تماس و شماره راه دوری که با یک تماس متصل شده است را مشخص کند."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"خواندن شناسه و وضعیت اصلی ارتباط دوربرد"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"به برنامه اجازه می‌دهد به ویژگی‌های اصلی ارتباط دوربرد دستگاه دسترسی داشته باشد."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"برقرار کردن تماس‌ها ازطریق سیستم"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"به برنامه امکان می‌دهد برای بهبود تجربه تماس، تماس‌هایش را ازطریق سیستم برقرار کند."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"دیدن و کنترل تماس‌ها ازطریق سیستم."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"برای حذف مدل چهره‌تان ضربه بزنید، سپس چهره‌تان را دوباره اضافه کنید"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"راه‌اندازی «قفل‌گشایی با چهره»"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"برای باز کردن قفل تلفن خود به آن نگاه کنید"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"‏برای استفاده از «قفل‌گشایی با چهره»، "<b>"دسترسی به دوربین"</b>" را در «تنظیمات &gt; حریم‌خصوصی» روشن کنید"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"راه‌اندازی روش‌های بیشتر برای باز کردن قفل"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"برای افزودن اثر انگشت، ضربه بزنید"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"قفل‌گشایی با اثر انگشت"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"آماده‌سازی <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"درحال آغاز کردن برنامه‌ها."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"درحال اتمام راه‌اندازی."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"صفحه‌نمایش خاموش شود؟"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"هنگام راه‌اندازی اثر انگشتتان، دکمه روشن/ خاموش را فشار دادید.\n\nاین کار معمولاً صفحه‌نمایش را خاموش می‌کند."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"خاموش شود"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"لغو شود"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"راه‌اندازی ادامه یابد؟"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"دکمه روشن/ خاموش را فشار دادید — این کار معمولاً صفحه‌نمایش را خاموش می‌کند.\n\nهنگام راه‌اندازی اثر انگشت، آرام ضربه بزنید."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"خاموش کردن صفحه"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ادامه راه‌اندازی"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"تأیید اثر انگشت را ادامه می‌دهید؟"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"دکمه روشن/ خاموش را فشار دادید — این کار معمولاً صفحه‌نمایش را خاموش می‌کند.\n\nبرای تأیید اثر انگشتتان، آرام ضربه بزنید."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"خاموش کردن صفحه"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ادامه"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> در حال اجرا"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"برای برگشت به بازی، ضربه بزنید"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"انتخاب بازی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index b63e72910280..ff17fc1ceb96 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Antaa sovelluksen soittaa puheluita pikaviestipalvelun avulla ilman käyttäjän toimia."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lue puhelimen tila ja identiteetti"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Antaa sovelluksen käyttää laitteen puhelinominaisuuksia. Sovellus voi määrittää puhelinnumeron ja laitteen tunnuksen, puhelun tilan sekä soitetun numeron."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lukea tavallisten puheluominaisuuksien tila- ja identiteettitiedot"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Sallii sovellukselle pääsyn laitteen tavallisiin puheluominaisuuksiin."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ohjata puhelut järjestelmän kautta"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Tämä sallii sovelluksen ohjata puhelut järjestelmän kautta, mikä auttaa parantamaan puhelujen laatua."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"nähdä puhelut ja päättää niistä järjestelmässä"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Poista kasvomalli napauttamalla ja lisää sitten kasvosi uudelleen"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Ota kasvojentunnistusavaus käyttöön"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Avaa puhelimesi lukitus katsomalla laitetta"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Jos haluat käyttää kasvojentunnistusavausta, valitse Asetukset &gt; Yksityisyys ja laita "<b>"Pääsy kameraan"</b>" päälle"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Ota käyttöön lisää tapoja avata lukitus"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Napauta lisätäksesi sormenjälki"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Sormenjälkiavaus"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Valmistellaan: <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Käynnistetään sovelluksia."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Viimeistellään päivitystä."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Sammutetaanko näyttö?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Painoit virtapainiketta ottaessasi sormenjäljen käyttöön.\n\nTämä saa yleensä näytön sammumaan."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Laita pois päältä"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Peru"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Haluatko jatkaa?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Painoit virtapainiketta, mikä yleensä sammuttaa näytön.\n\nKosketa painiketta kevyesti tallentaessasi sormenjälkeä."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Sammuta näyttö"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Jatka käyttöönottoa"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Jatketaanko sormenjäljen vahvistamista?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Painoit virtapainiketta, mikä yleensä sammuttaa näytön.\n\nVahvista sormenjälki koskettamalla painiketta kevyesti."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Sammuta näyttö"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Jatka"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> käynnissä"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Palaa peliin napauttamalla"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Valitse peli"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index cae01af7759d..e367f320ba2b 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permet à l\'application d\'utiliser le service IMS pour faire des appels sans votre intervention."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"voir l\'état et l\'identité du téléphone"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permet à l\'application d\'accéder aux fonctionnalités téléphoniques de l\'appareil. Cette autorisation permet à l\'application de déterminer le numéro de téléphone et les identifiants de l\'appareil, si un appel est actif et le numéro distant connecté par un appel."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"Lire l\'état et l\'identité de la téléphonie de base"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permet à l\'application d\'accéder aux fonctionnalités de téléphonie de base de l\'appareil."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"acheminer les appels dans le système"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permet à l\'application d\'acheminer ses appels dans le système afin d\'améliorer l\'expérience d\'appel."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"afficher et gérer les appels à l\'aide du système."</string>
@@ -622,6 +624,8 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Touchez pour supprimer votre modèle facial, puis ajoutez votre visage de nouveau"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurer le déverrouillage par reconnaissance faciale"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Déverrouillez votre téléphone en le regardant"</string>
+ <!-- no translation found for face_sensor_privacy_enabled (7407126963510598508) -->
+ <skip />
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurer d\'autres méthodes de déverrouillage"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Touchez pour ajouter une empreinte digitale"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Déverrouillage par empreinte digitale"</string>
@@ -1276,10 +1280,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Préparation de <xliff:g id="APPNAME">%1$s</xliff:g> en cours…"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Lancement des applications…"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalisation de la mise à jour."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Éteindre l\'écran?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Pendant que vous configuriez votre empreinte digitale, vous avez appuyé sur l\'interrupteur.\n\nAppuyer sur ce bouton ferme habituellement l\'écran."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Éteindre"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Annuler"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Poursuivre la configuration?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Vous avez appuyé sur le l\'interrupteur – cette action éteint habituellement l\'écran.\n\nEssayez de toucher légèrement pendant la configuration de votre empreinte digitale."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Éteindre l\'écran"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Poursuivre configu."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Poursuivre vérifica. empreinte digitale?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Vous avez appuyé sur le l\'interrupteur – cette action éteint habituellement l\'écran.\n\nEssayez de toucher légèrement pour vérifier votre empreinte digitale."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Éteindre l\'écran"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuer"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Touchez pour revenir au jeu"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choisissez un jeu"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 3955c9f65cdf..e00304ba3d64 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permet à l\'application d\'utiliser le service IMS pour passer des appels sans votre intervention."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"Voir l\'état et l\'identité du téléphone"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permet à l\'application d\'accéder aux fonctionnalités téléphoniques de l\'appareil. Cette autorisation permet à l\'application de déterminer le numéro de téléphone et les identifiants de l\'appareil, si un appel est actif et le numéro distant connecté par un appel."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lire l\'état et l\'identité de téléphonie de base"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Autorise l\'appli à accéder aux fonctionnalités de téléphonie de base de l\'appareil."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"acheminer les appels via le système"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Autorise l\'application à acheminer les appels via le système afin d\'optimiser le confort d\'utilisation."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"voir et contrôler les appels via le système."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Appuyez pour supprimer votre empreinte faciale, puis ajoutez de nouveau votre visage"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurer le déverrouillage par reconnaissance faciale"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Déverrouillez votre téléphone en le regardant"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pour utiliser Face Unlock, activez "<b>"Accès à l\'appareil photo"</b>" dans Paramètres &gt; Confidentialité"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurer d\'autres méthodes de déverrouillage"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Appuyez pour ajouter une empreinte digitale"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Déverrouillage par empreinte digitale"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Préparation de <xliff:g id="APPNAME">%1$s</xliff:g> en cours…"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Lancement des applications…"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalisation de la mise à jour."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Éteindre l\'écran ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Vous avez appuyé sur le bouton Marche/Arrêt pendant la configuration de votre empreinte digitale.\n\nCela éteint généralement l\'écran."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Éteindre"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Annuler"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Poursuivre la configuration ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Vous avez appuyé sur le bouton Marche/Arrêt, ce qui éteint généralement l\'écran.\n\nEssayez d\'appuyer doucement pendant la configuration de votre empreinte digitale."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Éteindre l\'écran"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Poursuivre"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuer de valider votre empreinte ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Vous avez appuyé sur le bouton Marche/Arrêt, ce qui éteint généralement l\'écran.\n\nPour valider votre empreinte digitale, appuyez plus doucement."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Éteindre l\'écran"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuer"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Appuyez pour revenir au jeu"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choisir un jeu"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index c07287ae9825..6f9ee8fb76a8 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite que a aplicación use o servizo de IMS para facer chamadas sen a túa intervención."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ler o estado e a identidade do teléfono"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite á aplicación acceder ás funcións de teléfono do dispositivo. Con este permiso a aplicación pode determinar o número de teléfono e os ID do dispositivo, se unha chamada está activa e o número remoto conectado mediante unha chamada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ler a identidade e o estado das funcións básicas de telefonía"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permítelle á aplicación acceder ás funcións básicas de telefonía do dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"dirixir as chamadas a través do sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite á aplicación dirixir as súas chamadas a través do sistema para mellorar a túa experiencia durante as chamadas."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"consultar e controlar as chamadas a través do sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toca para eliminar o teu modelo facial e despois engade de novo a cara"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Mira o teléfono para desbloquealo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar o desbloqueo facial, activa "<b>"Acceso á cámara"</b>" en Configuración &gt; Privacidade"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura máis maneiras de desbloquear o dispositivo"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca para engadir unha impresión dixital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo mediante impresión dixital"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando aplicacións."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Está finalizando o arranque"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Queres apagar a pantalla?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Premiches o botón de acendido mentres configurabas a impresión dixital.\n\nAo realizar esta acción, normalmente apágase a pantalla."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Apagar"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancelar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Queres continuar coa configuración?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Premiches o botón de acendido, o que adoita facer que se apague a pantalla.\n\nProba a dar un toque suave namentres configuras a impresión dixital."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Desactivar pantalla"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Seguir configurando"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Queres seguir verificando a impresión?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Premiches o botón de acendido, o que adoita facer que se apague a pantalla.\n\nProba a dar un toque suave para verificar a impresión dixital."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desactivar pantalla"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuar"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> está en execución"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Toca para volver ao xogo"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Escolle un xogo"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 55289549aab9..533d3fbacbbb 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"તમારા હસ્તક્ષેપ વગર કૉલ્સ કરવા માટે IMS સેવાનો ઉપયોગ કરવાની એપ્લિકેશનને મંજૂરી આપે છે."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ફોન સ્થિતિ અને ઓળખ વાંચો"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"એપ્લિકેશનને ફોન સુવિધાઓને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ પરવાનગી એપ્લિકેશનને ફોન નંબર અને ઉપકરણ ID, કૉલ સક્રિય છે અને કોઈ કૉલ દ્વારા કનેક્ટ થયેલ રિમોટ નંબર નિર્ધારિત કરવાની મંજૂરી આપે છે."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ટેલિફોન માટેનું મૂળભૂત સ્ટેટસ અને ઓળખ વાંચો"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ડિવાઇસની ટેલિફોન માટેની મૂળભૂત સુવિધાઓ ઍક્સેસ કરવાની મંજૂરી ઍપને આપે છે."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"સિસ્ટમ મારફતે કૉલ બીજે વાળો"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"કૉલિંગ અનુભવ સુધારવા માટે ઍપ્લિકેશનને સિસ્ટમ મારફતે કૉલ બીજે વાળવાની મંજૂરી આપે છે."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"સિસ્ટમ મારફતે કૉલ જુઓ અને નિયંત્રિત કરો."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"તમારા ચહેરાનું મૉડલ ડિલીટ કરવા માટે ટૅપ કરો, પછી તમારો ચહેરો ફરીથી ઉમેરો"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ફેસ અનલૉક સુવિધાનું સેટઅપ કરો"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"તમારા ફોનની તરફ જોઈને તેને અનલૉક કરો"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ફેસ અનલૉક સુવિધાનો ઉપયોગ કરવા માટે, સેટિંગ &gt; પ્રાઇવસીમાં જઈને "<b>"કૅમેરા ઍક્સેસ"</b>" ચાલુ કરો"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"અનલૉક કરવાની બીજી રીતોનું સેટઅપ કરો"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ફિંગરપ્રિન્ટ ઉમેરવા માટે ટૅપ કરો"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ફિંગરપ્રિન્ટ અનલૉક"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> તૈયાર કરી રહ્યું છે."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ઍપ્લિકેશનો શરૂ કરી રહ્યાં છે."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"બૂટ સમાપ્ત કરી રહ્યાં છે."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"સ્ક્રીન બંધ કરીએ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"તમારી ફિંગરપ્રિન્ટની સેટિંગનું સેટઅપ કરતી વખતે, તમે પાવર બટન દબાવ્યું.\n\nઆનાથી સામાન્ય રીતે તમારી સ્ક્રીન બંધ થઈ જાય છે."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"બંધ કરો"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"રદ કરો"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"શું સેટઅપ કરવાનું ચાલુ રાખીએ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"તમે પાવર બટન દબાવ્યું છે — જેનાથી સામાન્ય રીતે સ્ક્રીન બંધ થઈ જાય છે.\n\nતમારી ફિંગરપ્રિન્ટનું સેટઅપ કરતી વખતે હળવેથી ટૅપ કરવાનો પ્રયાસ કરો."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"સ્ક્રીન બંધ કરો"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"સેટઅપ આગળ વધારો"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"શું તમારી ફિંગરપ્રિન્ટની ચકાસણી કરીએ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"તમે પાવર બટન દબાવ્યું છે — જેનાથી સામાન્ય રીતે સ્ક્રીન બંધ થઈ જાય છે.\n\nતમારી ફિંગરપ્રિન્ટની ચકાસણી કરવા માટે, તેને હળવેથી ટૅપ કરવાનો પ્રયાસ કરો."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"સ્ક્રીન બંધ કરો"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"આગળ વધો"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ચાલુ છે"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ગેમ પર પાછા આવવા માટે ટૅપ કરો"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ગેમ પસંદ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index cb6de5ccf4e5..84594373bc76 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"आपके हस्‍तक्षेप के बिना कॉल करने के लिए, ऐप को IMS सेवा का उपयोग करने देती है."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"फ़ोन की स्‍थिति और पहचान पढ़ें"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ऐप्स को डिवाइस की फ़ोन सुविधाओं तक पहुंचने देता है. यह अनुमति ऐप्स को फ़ोन नंबर और डिवाइस आईडी, कॉल सक्रिय है या नहीं, और कॉल द्वारा कनेक्ट किया गया दूरस्‍थ नंबर तय करने देती है."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"टेलिफ़ोनी सुविधाओं की सामान्य स्थिति और उनकी पहचान से जुड़ी जानकारी पढ़ें"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ऐसा करने पर, इस ऐप्लिकेशन को डिवाइस की सामान्य टेलिफ़ोनी सुविधाएं ऐक्सेस करने की अनुमति मिलती है."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"सिस्टम के माध्यम से कॉल रूट करें"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"कॉल करने के अनुभव को बेहतर बनाने के लिए ऐप्लिकेशन को सिस्टम के माध्यम से उसके कॉल रूट करने देती है."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"सिस्टम के ज़रिए कॉल देखना और नियंत्रित करना."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"अपने चेहरे का मॉडल मिटाने के लिए टैप करें. इसके बाद, अपना चेहरा फिर से रजिस्टर करें"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"फे़स अनलॉक की सुविधा सेट अप करें"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"अपने फ़ोन की तरफ़ देखकर उसे अनलॉक करें"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"फ़ेस अनलॉक की सुविधा का इस्तेमाल करने के लिए, सेटिंग और निजता में जाकर, "<b>"कैमरे का ऐक्सेस"</b>" चालू करें"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"फ़ोन को अनलॉक करने के दूसरे तरीके सेट अप करें"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"फ़िंगरप्रिंट जोड़ने के लिए टैप करें"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"फ़िंगरप्रिंट अनलॉक"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> तैयार हो रहा है."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ऐप्स प्रारंभ होने वाले हैं"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"बूट खत्म हो रहा है."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"स्क्रीन बंद करें?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"अपना फ़िंगरप्रिंट सेट अप करते समय, आपने पावर बटन दबाया.\n\nआम तौर पर, इससे स्क्रीन बंद हो जाती है."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"बंद करें"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"अभी नहीं"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"सेट अप जारी रखना है?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"आपने पावर बटन दबाया - आम तौर पर, इससे स्क्रीन बंद हो जाती है.\n\nअपना फ़िंगरप्रिंट सेट अप करते समय, बटन को हल्के से टैप करके देखें."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"स्क्रीन बंद करें"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"सेट अप जारी रखें"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"फ़िंगरप्रिंट की पुष्टि करना जारी रखना है?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"आपने पावर बटन दबाया - आम तौर पर, इससे स्क्रीन बंद हो जाती है.\n\nअपने फ़िंगरप्रिंट की पुष्टि करने के लिए, बटन पर हल्के से टैप करके देखें."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"स्क्रीन बंद करें"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"जारी रखें"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> चल रही है"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"गेम पर वापस जाने के लिए टैप करें"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"गेम चुनें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 1dc25176d5ed..69905ecd8554 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -474,6 +474,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Omogućuje aplikaciji upotrebu usluge izravnih poruka za uspostavljanje poziva bez vaše intervencije."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"čitanje statusa i identiteta telefona"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Aplikaciji omogućuje pristup telefonskim značajkama uređaja. Ta dozvola aplikaciji omogućuje utvrđivanje telefonskog broja i ID-ova uređaja, je li poziv aktivan te udaljeni broj koji je povezan pozivom."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"čitanje osnovnih telefonskih statusa i identiteta"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Dopušta aplikacijama pristup osnovnim telefonskim značajkama uređaja."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"preusmjeravati pozive putem sustava"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Omogućuje aplikaciji da preusmjerava pozive putem sustava radi poboljšanja doživljaja."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"vidjeti i kontrolirati pozive putem sustava."</string>
@@ -625,6 +627,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da biste izbrisali model lica, a zatim ponovo dodajte svoje lice"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Postavite otključavanje licem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon gledajući u njega"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da biste koristili otključavanje licem, uključite opciju "<b>"Pristup kameri"</b>" u odjeljku Postavke &gt; Privatnost"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Postavite više načina otključavanja"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da biste dodali otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
@@ -1296,10 +1299,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Pripremanje aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Pokretanje aplikacija."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Završetak inicijalizacije."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Isključiti zaslon?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Prilikom postavljanja otiska prsta pritisnuli ste tipku za uključivanje/isključivanje.\n\nTom se radnjom obično isključuje zaslon."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Isključi"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Odustani"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Želite li nastaviti s postavljanjem?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnuli ste tipku za uključivanje/isključivanje, čime se obično isključuje zaslon.\n\nPokušajte lagano dodirnuti dok postavljate svoj otisak prsta."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Isključi zaslon"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Nastavi postavljanje"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Nastaviti s potvrđivanjem otiska prsta?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnuli ste tipku za uključivanje/isključivanje, čime se obično isključuje zaslon.\n\nPokušajte lagano dodirnuti da biste potvrdili svoj otisak prsta."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Isključi zaslon"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Nastavi"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Izvodi se <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Dodirnite za povratak na igru"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Odabir igre"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 34f99630436f..4f040161709d 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Az alkalmazás az IMS-szolgáltatást használhatja híváskezdeményezéshez az Ön közbeavatkozása nélkül."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"telefonállapot és azonosító olvasása"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Lehetővé teszi az alkalmazás számára, hogy hozzáférjen az eszköz telefonálási funkcióihoz. Az engedéllyel rendelkező alkalmazás meghatározhatja a telefonszámot és eszközazonosítókat, hogy egy hívás aktív-e, valamint híváskor a másik fél telefonszámát."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"alapvető telefonállapot és identitás olvasása"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Lehetővé teszi az alkalmazás számára, hogy hozzáférjen az eszköz alapvető telefonos szolgáltatásaihoz."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"rendszeren keresztüli hívásirányítás"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"A telefonálási élmény javítása érdekében lehetővé teszi az alkalmazás számára a rendszeren keresztüli hívásirányítást."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Hívások megtekintése és vezérlése a rendszeren keresztül"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Koppintson arcmodellje törléséhez, majd készítsen újat"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Az Arcalapú feloldás beállítása"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Feloldhatja a zárolást úgy, hogy ránéz a telefonra"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Az Arcalapú feloldás funkció használatához kapcsolja be a "<b>"Hozzáférés a kamerához"</b>" beállítást a Beállítások &gt; Adatvédelem szakaszban."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"További feloldási módszerek beállítása"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Koppintson ide ujjlenyomat hozzáadásához"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Feloldás ujjlenyomattal"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> előkészítése."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Kezdő alkalmazások."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Rendszerindítás befejezése."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Kikapcsolja a képernyőt?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Ujjlenyomata beállítása közben megnyomta a bekapcsológombot.\n\nEz általában kikapcsolja a képernyőt."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Kikapcsolás"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Mégse"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Folytatja a beállítást?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Megnyomta a bekapcsológombot — ezzel általában kikapcsol a képernyő.\n\nPróbáljon finoman rákoppintani az ujjlenyomat beállítása közben."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Kikapcsolom"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Beállítás folytatása"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Folytatja az ujjlenyomat ellenőrzését?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Megnyomta a bekapcsológombot — ezzel általában kikapcsol a képernyő.\n\nPróbáljon finoman rákoppintani az ujjlenyomat ellenőrzéséhez."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Kikapcsolom"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Tovább"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> fut"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Koppintson ide a játékhoz való visszatéréshez"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Játék kiválasztása"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 03f9e1d17e2c..141780e3a99d 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Թույլ է տալիս հավելվածին IMS ծառայության միջոցով կատարել զանգեր՝ առանց ձեր միջամտության:"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"կարդալ հեռախոսի կարգավիճակը և ինքնությունը"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Թույլ է տալիս հավելվածին օգտագործել սարքի հեռախոսային գործիքները: Այս թույլտվությունը հավելվածին հնարավորություն է տալիս որոշել հեռախոսահամարը և սարքի ID-ները, արդյոք զանգը ակտիվ է և միացված զանգի հեռակա հեռախոսահամարը:"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"Կարդալ հիմնական հեռախոսային գործառույթների կարգավիճակը և նույնականացնող տվյալներ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Հավելվածին հասանելի է դարձնում սարքի հիմնական հեռախոսային գործառույթները:"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"զանգերն ուղարկել համակարգի միջոցով"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Հավելվածին թույլ է տալիս իր զանգերն ուղարկել համակարգի միջոցով՝ կապի որակը բարձրացնելու նպատակով։"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Զանգերի դիտում և վերահսկում համակարգի միջոցով"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Հպեք՝ ձեր դեմքի նմուշը ջնջելու համար, այնուհետև նորից ավելացրեք այն:"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Կարգավորեք դեմքով ապակողպումը"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ապակողպելու համար պարզապես նայեք հեռախոսին"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Դեմքով ապակողպումն օգտագործելու համար անցեք Կարգավորումներ &gt; Գաղտնիություն և տրամադրեք "<b>"տեսախցիկն օգտագործելու թույլտվություն"</b>"։"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Կարգավորեք ապակողպելու այլ եղանակներ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Հպեք՝ մատնահետք ավելացնելու համար"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Մատնահետքով ապակողպում"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածը պատրաստվում է:"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Հավելվածները մեկնարկում են:"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Բեռնումն ավարտվում է:"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Անջատե՞լ էկրանը"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Մատնահետքը կարգավորելու ժամանակ դուք սեղմել եք սնուցման կոճակը։\n\nՍովորաբար դրա արդյունքում էկրանն անջատվում է։"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Անջատել"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Չեղարկել"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Շարունակե՞լ կարգավորումը"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Դուք սեղմել եք սնուցման կոճակը։ Սովորաբար դրա արդյունքում էկրանն անջատվում է։\n\nՄատնահետքը ավելացնելու համար թեթևակի հպեք կոճակին։"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Անջատել էկրանը"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Շարունակել գրանցումը"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Շարունակե՞լ մատնահետքի սկանավորումը"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Դուք սեղմել եք սնուցման կոճակը։ Սովորաբար դրա արդյունքում էկրանն անջատվում է։\n\nՄատնահետքը սկանավորելու համար թեթևակի հպեք կոճակին։"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Անջատել էկրանը"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Շարունակել"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g>-ն աշխատում է"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Հպեք՝ խաղին վերադառնալու համար"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Ընտրեք խաղ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index db97591db32b..e1e8ea91a04c 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Memungkinkan aplikasi menggunakan layanan IMS untuk melakukan panggilan tanpa campur tangan Anda."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"baca identitas dan status ponsel"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Memungkinkan aplikasi mengakses fitur telepon perangkat. Izin ini memungkinkan aplikasi menentukan nomor telepon dan ID perangkat, apakah suatu panggilan aktif atau tidak, dan nomor jarak jauh yang terhubung oleh sebuah panggilan."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"membaca identitas dan status telepon dasar"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Memungkinkan aplikasi mengakses fitur telepon dasar perangkat."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"sambungkan panggilan telepon melalui sistem"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Mengizinkan aplikasi menyambungkan panggilan telepon melalui sistem untuk menyempurnakan pengalaman menelepon."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"melihat dan mengontrol panggilan melalui sistem."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ketuk untuk menghapus model wajah, lalu tambahkan wajah Anda lagi"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Siapkan Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Buka kunci ponsel dengan melihat ke ponsel"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Untuk menggunakan Face Unlock, aktifkan "<b>"Akses kamera"</b>" di Setelan &gt; Privasi"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Siapkan lebih banyak cara untuk membuka kunci"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ketuk untuk menambahkan sidik jari"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Menyiapkan <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Memulai aplikasi."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Menyelesaikan boot."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Nonaktifkan layar?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Saat menyiapkan sidik jari, Anda menekan Tombol daya.\n\nTindakan ini biasanya akan menonaktifkan layar."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Nonaktifkan"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Batal"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Lanjutkan penyiapan?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Anda menekan tombol daya; tindakan ini biasanya akan menonaktifkan layar.\n\nCoba ketuk lembut saat menyiapkan sidik jari Anda."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Nonaktifkan layar"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Lanjutkan penyiapan"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Lanjutkan verifikasi sidik jari?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Anda menekan tombol daya; tindakan ini biasanya akan menonaktifkan layar.\n\nCoba ketuk lembut untuk memverifikasi sidik jari Anda."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Nonaktifkan layar"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Lanjutkan"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> sedang berjalan"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ketuk untuk kembali ke game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pilih game"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index d70a66bd6458..0678ab2034a2 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Leyfir forriti að nota IMS-þjónustu til að hringja án inngrips frá þér."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lesa stöðu símans og auðkenni"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Leyfir forriti að fá aðgang að símaeiginleikum tækisins. Þessi heimild gerir forritinu kleift að komast að símanúmeri og auðkennum tækisins, hvort símtal er í gangi og símanúmeri viðmælanda í símtali."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lesa stöðu og auðkenni grunneiginleika símaþjónustu"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Leyfir forritinu að fá aðgang að grunneiginleikum símaþjónustu tækisins."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"senda símtöl gegnum kerfið"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Heimilar forritinu að senda símtöl sín gegnum kerfið til að bæta gæði símtalsins."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"sjá og stjórna símtölum í gegnum kerfið."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ýttu til að eyða andlitslíkaninu og skráðu svo andlitið aftur"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Setja upp andlitskenni"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Taktu símann úr lás með því að horfa á hann"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Þú verður að kveikja á "<b>"aðgangi að myndavél"</b>" í „Stillingar &gt; persónuvernd“ til að nota andlitskenni"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Settu upp fleiri leiðir til að taka úr lás"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ýttu til að bæta við fingrafari"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingrafarskenni"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Undirbýr <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Ræsir forrit."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Lýkur ræsingu."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Slökkva á skjá?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Þú ýttir á aflrofann þegar þú varst að skrá fingrafarið þitt.\n\nYfirleitt slekkur það á skjánum."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Slökkva"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Hætta við"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Halda uppsetningu áfram?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Þú ýttir á aflrofann. Yfirleitt slekkur það á skjánum.\n\nPrófaðu að ýta laust þegar þú setur upp fingrafarið."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Slökkva á skjá"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Halda uppsetningu áfram"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Halda áfram að staðfesta fingrafarið?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Þú ýttir á aflrofann. Yfirleitt slekkur það á skjánum.\n\nPrófaðu að ýta laust til að staðfesta fingrafarið þitt."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Slökkva á skjá"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Áfram"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> er í gangi"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ýttu til að fara aftur í leik"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Velja leik"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 91df4bdb7ad3..a63fbd82c510 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Consente all\'app di utilizzare il servizio IMS per fare chiamate senza il tuo intervento."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lettura stato e identità telefono"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Consente all\'applicazione di accedere alle funzioni telefoniche del dispositivo. Questa autorizzazione consente all\'applicazione di determinare il numero di telefono e gli ID dei dispositivi, se una chiamata è attiva e il numero remoto connesso da una chiamata."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lettura di identità e stato telefonia"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Consente all\'app di accedere alle funzionalità di telefonia di base del dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"indirizzamento delle chiamate tramite il sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Consente all\'app di indirizzare le proprie chiamate tramite il sistema al fine di migliorare l\'esperienza di chiamata."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"visualizzazione e controllo delle chiamate tramite il sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tocca per eliminare il tuo modello del volto e poi riaggiungi il tuo volto"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura lo sblocco con il volto"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Sblocca il telefono guardandolo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Per utilizzare lo sblocco con il volto, attiva "<b>"l\'accesso alla fotocamera"</b>" in Impostazioni &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura altri modi per sbloccare"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tocca per aggiungere un\'impronta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Sblocco con l\'impronta"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> in preparazione."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Avvio app."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Conclusione dell\'avvio."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vuoi disattivare lo schermo?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Durante la configurazione della tua impronta hai premuto il tasto di accensione.\n\nGeneralmente questa azione comporta la disattivazione dello schermo."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Disattiva"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Annulla"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Vuoi continuare la configurazione?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Hai premuto il tasto di accensione; in genere questa azione disattiva lo schermo.\n\nProva a toccare leggermente il tasto di accensione durante la configurazione della tua impronta."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Disattiva lo schermo"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continua configuraz."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vuoi continuare a verificare l\'impronta?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Hai premuto il tasto di accensione; in genere questa azione disattiva lo schermo.\n\nProva a toccare leggermente il tasto di accensione per verificare la tua impronta."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Disattiva lo schermo"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continua"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> in esecuzione"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tocca per tornare al gioco"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Scegli gioco"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index a16c682038be..7a594d821c62 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"‏מאפשרת לאפליקציה להשתמש בשירות ה-IMS לביצוע שיחות ללא התערבות שלך."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"קריאת הסטטוס והזהות של הטלפון"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"מאפשרת לאפליקציה לגשת לתכונות הטלפון של המכשיר. ההרשאה הזו מתירה לאפליקציה לגלות את מספר הטלפון ואת מזהי המכשיר, אם השיחה פעילה ואת המספר המרוחק המחובר באמצעות שיחה."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"קריאה של זהות וסטטוס טלפוניים בסיסיים"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ההרשאה מאפשרת לאפליקציה לגשת לתכונות הטלפוניות הבסיסיות של המכשיר."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ניתוב שיחות דרך המערכת"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"מאפשרת לאפליקציה לנתב את השיחות דרך המערכת כדי לשפר את חוויית השיחה."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"הצגת שיחות ושליטה בהן באמצעות המערכת."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"יש להקיש כדי למחוק את התבנית לזיהוי הפנים, ואז להוסיף תבנית חדשה לזיהוי הפנים"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"הגדרת התכונה \'פתיחה ע\"י זיהוי הפנים\'"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"יש להביט בטלפון כדי לבטל את נעילתו"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"‏כדי להשתמש בתכונה \'פתיחה ע\"י זיהוי הפנים\', יש להפעיל את ה"<b>"גישה למצלמה"</b>" בהגדרות &gt; פרטיות"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"אפשר להגדיר דרכים נוספות לביטול נעילה"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"יש להקיש כדי להוסיף טביעת אצבע"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ביטול הנעילה בטביעת אצבע"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"המערכת מכינה את <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"מתבצעת הפעלה של אפליקציות."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"תהליך האתחול בשלבי סיום."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"לכבות את המסך?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"כשהגדרת את טביעת האצבע, לחצת על לחצן ההפעלה.\n\nלרוב, הפעולה הזו מכבה את המסך."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"כיבוי"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ביטול"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"להמשיך בהגדרה?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"לחצת על לחצן ההפעלה – בדרך כלל הפעולה הזו מכבה את המסך.\n\nעליך לנסות להקיש בעדינות במהלך ההגדרה של טביעת האצבע שלך."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"כיבוי המסך"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"להמשך ההגדרה"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"להמשיך לאמת את טביעת האצבע שלך?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"לחצת על לחצן ההפעלה – בדרך כלל הפעולה הזו מכבה את המסך.\n\nעליך לנסות להקיש בעדינות כדי לאמת את טביעת האצבע שלך."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"כיבוי המסך"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"המשך"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"האפליקציה <xliff:g id="APP">%1$s</xliff:g> פועלת"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"יש להקיש כדי לחזור למשחק"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"בחירת משחק"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index ff7a868cdb70..241105f1b6fe 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"IMSサービスがユーザー操作なしで電話をかけることをアプリに許可します。"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"デバイス情報と ID の読み取り"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"デバイスの電話機能へのアクセスをアプリに許可します。これにより、電話番号、デバイスID、通話中かどうか、通話相手の電話番号をアプリから特定できるようになります。"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"電話の基本的なステータスと ID の読み取り"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"デバイスの基本的な電話機能へのアクセスをアプリに許可します。"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"システム経由での通話転送"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"通話環境の改善のために、システム経由での通話転送をアプリに許可します。"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"システム経由の通話の表示と操作。"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"タップして顔モデルを削除してから、改めて顔を追加してください"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"顔認証の設定"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"スマートフォンに顔を向けるとロックが解除されます"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"顔認証を使用するには、[設定] &gt; [プライバシー] で"<b>"カメラへのアクセス"</b>"を有効にしてください"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"その他のロック解除方法の設定"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"タップすると指紋が追加されます"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指紋認証"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>をペア設定しています。"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"アプリを起動しています。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ブートを終了しています。"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"画面を OFF にしますか?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"指紋の設定中に電源ボタンが押されました。\n\n通常、この操作により画面が OFF になります。"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"OFF にする"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"キャンセル"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"設定を続行しますか?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"電源ボタンを押しました。通常、この操作で画面が OFF になります。\n\n指紋を設定する場合は電源ボタンに軽く触れてみましょう。"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"画面を OFF にする"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"設定を続行"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"指紋の確認を続行しますか?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"電源ボタンを押しました。通常、この操作で画面が OFF になります。\n\n指紋を確認するには、電源ボタンに軽く触れてみましょう。"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"画面を OFF にする"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"続行"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g>を実行中"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"タップするとゲームに戻ります"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ゲームの選択"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index dbc7bf59dae0..2137e0feee53 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"აპს შეეძლება, გამოიყენოს IMS სერვისი ზარების თქვენი ჩარევის გარეშე განსახორციელებლად."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ტელეფონის სტატუსისა და იდენტობის წაკითხვა"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"აპს შეეძლება ჰქონდეს წვდომა მოწყობილობის სატელეფონო ფუნქციებზე. აპმა მსგავსი უფლებით შეძლებს დაადგინოს ტელეფონის ნომერი, მისი სერიული გამოცემა, აქტიური ზარი, დაკავშირებული ნომერი და მსგავსი."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"წაიკითხეთ ტელეფონის ძირითადი სტატუსი და აიდი"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"აპს აძლევს მოწყობილობის ძირითად სატელეფონო ფუნქციებზე წვდომას."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ზარების სისტემის მეშვეობით მარშრუტიზაცია"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"დარეკვის ხარისხის გაუმჯობესების მიზნით, აპს ზარების სისტემის მეშვეობით მარშრუტიზაციის საშუალებას აძლევს."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ზარების ნახვა და გაკონტროლება სისტემის მეშვეობით."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"შეეხეთ თქვენი სახის მოდელის წასაშლელად, შემდეგ დაამატეთ სახე ხელახლა"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"სახით განბლოკვის პარამეტრების დაყენება"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"განბლოკეთ თქვენი ტელეფონი შეხედვით"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"იმისთვის, რომ სახით განბლოკვით ისარგებლოთ, ჩართეთ "<b>"კამერაზე წვდომა"</b>" პარამეტრებსა და კონფიდენციალურობაში"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"დააყენეთ განბლოკვის სხვა ხერხები"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"შეეხეთ თითის ანაბეჭდის დასამატებლად"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"თითის ანაბეჭდით განბლოკვა"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"ემზადება <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"აპების ჩართვა"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ჩატვირთვის დასასრული."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"გამოირთოს ეკრანი?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"თითის ანაბეჭდის დაყენებისას ჩართვის ღილაკს დააჭირეთ.\n\nეს, ჩვეულებრივ, თქვენს ეკრანს გამორთავს."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"გამორთვა"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"გაუქმება"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"გსურთ დაყენების გაგრძელება?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"თქვენ დააჭირეთ ჩართვის ღილაკს — ჩვეულებრივ, ის ეკრანს გამორთავს.\n\nთქვენი თითის ანაბეჭდის დაყენებისას ცადეთ მსუბუქად შეხება."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ეკრანის გამორთვა"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"დაყენების გაგრძელება"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"გსურთ ანაბეჭდის დადასტურების გაგრძელება?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"თქვენ დააჭირეთ ჩართვის ღილაკს — ჩვეულებრივ, ის ეკრანს გამორთავს.\n\nთქვენი თითის ანაბეჭდის დასადასტურებლად ცადეთ მსუბუქად შეხება."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ეკრანის გამორთვა"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"გაგრძელება"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> გაშვებულია"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"შეეხეთ თამაშში დასაბრუნებლად"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"აირჩიეთ თამაში"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index c1d421fc4e85..3042c643f4ab 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Сіздің қатысуыңызсыз қоңыраулар соғу үшін қолданбаға IMS қызметін пайдалануға рұқсат етеді."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"телефон күйін оқу немесе анықтау"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Қолданбаға құрылғыдағы телефон функцияларына кіру мүмкіндігін береді. Бұл рұқсат қолданбаға телефон нөмірі, құрылғы жеке анықтағышы, қоңырау белсенділігі және сол қоңырауға байланысты қашықтағы нөмірді анықтау мүмкіндігін береді."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"негізгі телефония күйі мен сәйкестендіру деректерін оқу"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Қолданбаға құрылғының негізгі телефония функцияларын пайдалануға рұқсат береді."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"қоңырауларды жүйе арқылы бағыттау"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Қоңырау шалу тәжірибесін жақсарту үшін қолданба қоңырауларды жүйе арқылы бағыттай алады."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"жүйе арқылы қоңырауларды көру және басқару."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Бет үлгісін жою үшін түртіңіз, содан соң жаңа бет үлгісін қосыңыз."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Бет тану функциясын реттеу"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Телефоныңызға қарап, оның құлпын ашыңыз."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Face Unlock функциясын пайдалану үшін \"Параметрлер &gt; Құпиялылық\" бөлімінен "<b>"Камераны пайдалану рұқсатын"</b>" қосыңыз."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Құлыпты ашудың басқа тәсілдерін реттеу"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Саусақ ізін қосу үшін түртіңіз."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Құлыпты саусақ ізімен ашу"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> дайындалуда."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Қолданбалар іске қосылуда."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Қосуды аяқтауда."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Экранды өшіру керек пе?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Саусақ ізіңізді орнату кезінде қуат түймесін басып қалдыңыз.\n\nБұл әрекет әдетте экранды өшіреді."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Өшіру"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Бас тарту"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Реттеуді жалғастырасыз ба?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Қуат түймесін бастыңыз. Бұл әдетте экранды өшіреді.\n\nСаусақ ізін реттеу үшін, оны жайлап түртіп көріңіз."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Экранды өшіру"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Реттеуді жалғастыру"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Саусақ ізін растауды жалғастырасыз ба?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Қуат түймесін бастыңыз. Бұл әдетте экранды өшіреді.\n\nСаусақ ізін растау үшін, оны жайлап түртіп көріңіз."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Экранды өшіру"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Жалғастыру"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> қосылған"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ойынды жалғастыру үшін түртіңіз"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Ойынды таңдаңыз"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 2889ac3c1b8f..81fbca5fe604 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"អនុញ្ញាតឲ្យកម្មវិធីនេះប្រើសេវាកម្ម IMS ដើម្បីធ្វើការហៅដោយគ្មានការអន្តរាគមន៍ពីអ្នក។"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"អាន​ស្ថានភាព និង​អត្តសញ្ញាណ​ទូរស័ព្ទ"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ឲ្យ​កម្មវិធី​ចូល​ដំណើរការ​លក្ខណៈ​ទូរស័ព្ទ​នៃ​ឧបករណ៍។ សិទ្ធិ​នេះ​​ឲ្យ​កម្មវិធី​កំណត់​លេខ​ទូរស័ព្ទ និង​លេខ​សម្គាល់​ឧបករណ៍ ថា​តើ​ការ​ហៅ​សកម្ម និង​លេខ​ពី​ចម្ងាយ​បាន​ភ្ជាប់​ដោយ​ការ​ហៅ។"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"អានអត្តសញ្ញាណ និងស្ថានភាពទូរសព្ទគោល"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"អនុញ្ញាតឱ្យកម្មវិធីចូលប្រើមុខងារទូរសព្ទគោលរបស់ឧបករណ៍។"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"បញ្ជូន​ការ​ហៅ​ទូរសព្ទ​តាមរយៈ​ប្រព័ន្ធ"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"អនុញ្ញាត​ឲ្យ​កម្មវិធី​នេះ​បញ្ជូន​ការ​ហៅ​ទូរសព្ទ​របស់វា​តាមរយៈ​ប្រព័ន្ធ ​ដើម្បី​ធ្វើ​ឲ្យ​ការ​ហៅ​ទូរសព្ទ​ប្រសើរ​ជាង​មុន។"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"មើល និង​គ្រប់គ្រង​ការហៅទូរសព្ទ​តាមរយៈប្រព័ន្ធ។"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ចុចដើម្បីលុប​គំរូមុខ​របស់អ្នក រួចបញ្ចូល​មុខរបស់អ្នក​ម្ដងទៀត"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"រៀបចំ​ការដោះសោ​តាមទម្រង់មុខ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ដោះសោទូរសព្ទ​របស់អ្នកដោយសម្លឹងមើលវា"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ដើម្បីប្រើមុខងារដោះសោតាមទម្រង់មុខ សូមបើក"<b>"ការចូលប្រើកាមេរ៉ា"</b>"នៅក្នុងការកំណត់ &gt; ឯកជនភាព"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"រៀបចំ​វិធីច្រើនទៀត​ដើម្បី​ដោះសោ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ចុច​ដើម្បីបញ្ចូល​ស្នាមម្រាមដៃ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ការដោះសោ​ដោយប្រើ​ស្នាមម្រាមដៃ"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"កំពុងរៀបចំ <xliff:g id="APPNAME">%1$s</xliff:g>។"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ចាប់ផ្ដើម​កម្មវិធី។"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"បញ្ចប់​ការ​ចាប់ផ្ដើម។"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"បិទ​អេក្រង់ឬ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"នៅពេលរៀបចំ​ស្នាមម្រាមដៃ​របស់អ្នក អ្នកបានចុច​ប៊ូតុងថាមពល។\n\nជាធម្មតា ការធ្វើបែបនេះ​បិទអេក្រង់​របស់អ្នក។"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"បិទ"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"បោះបង់"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"បន្ត​រៀបចំឬ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"អ្នកបានចុចប៊ូតុងថាមពល — ជាធម្មតាការធ្វើបែបនេះនឹងបិទអេក្រង់។\n\nសាកល្បងចុចថ្នមៗ ខណៈពេលកំពុងរៀបចំស្នាមម្រាមដៃរបស់អ្នក។"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"បិទ​អេក្រង់"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"បន្ត​រៀបចំ"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"បន្តផ្ទៀងផ្ទាត់ស្នាមម្រាមដៃរបស់អ្នកឬ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"អ្នកបានចុចប៊ូតុងថាមពល — ជាធម្មតាការធ្វើបែបនេះនឹងបិទអេក្រង់។\n\nសាកល្បងចុចថ្នមៗ ដើម្បីផ្ទៀងផ្ទាត់ស្នាមម្រាមដៃរបស់អ្នក។"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"បិទ​អេក្រង់"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"បន្ត"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> កំពុង​ដំណើរការ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ចុច​ដើម្បី​ត្រឡប់​ទៅ​ហ្គេមវិញ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ជ្រើសរើស​ហ្គេម"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index cc9771a23586..569f6a535349 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"ನಿಮ್ಮ ಮಧ್ಯಸ್ಥಿಕೆ ಇಲ್ಲದೆಯೇ ಕರೆಗಳನ್ನು ಮಾಡಲು IMS ಸೇವೆಯನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ಫೋನ್ ಸ್ಥಿತಿ ಮತ್ತು ಗುರುತಿಸುವಿಕೆಯನ್ನು ಓದಿ"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ಸಾಧನದ ಫೋನ್ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಈ ಅನುಮತಿಯು ಫೋನ್ ಸಂಖ್ಯೆ ಮತ್ತು ಸಾಧನದ ID ಗಳನ್ನು ನಿರ್ಧರಿಸಲು, ಕರೆಯು ಸಕ್ರಿಯವಾಗಿದೆಯೇ ಮತ್ತು ಕರೆಯ ಮೂಲಕ ರಿಮೋಟ್ ಸಂಖ್ಯೆಯು ಸಂಪರ್ಕಗೊಂಡಿವೆಯೇ ಎಂಬುದನ್ನೂ ನಿರ್ಧರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅವಕಾಶ ಕಲ್ಪಿಸುತ್ತದೆ."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ಮೂಲ ಟೆಲಿಫೋನ್ ಸ್ಥಿತಿ ಮತ್ತು ಗುರುತನ್ನು ಓದಿ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ಸಾಧನದ ಮೂಲ ಟೆಲಿಫೋನ್ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ಕರೆಗಳನ್ನು ಸಿಸ್ಟಂ ಮೂಲಕ ರವಾನಿಸಿ"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"ಕರೆಯ ಅನುಭವವನ್ನು ಸುಧಾರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ನ ಕರೆಗಳನ್ನು ಸಿಸ್ಟಂ ಮೂಲಕ ರವಾನಿಸಲು ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ಸಿಸ್ಟಂ ಮೂಲಕ ಕರೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿಯಂತ್ರಿಸಿ."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ನಿಮ್ಮ ಫೇಸ್ ಮಾಡೆಲ್ ಅನ್ನು ಅಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ, ನಂತರ ನಿಮ್ಮ ಫೇಸ್ ಮಾಡೆಲ್ ಅನ್ನು ಪುನಃ ಸೇರಿಸಿ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಿ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ಫೋನ್ ಅನ್ನು ನೋಡುವ ಮೂಲಕ ಅನ್‌ಲಾಕ್‌ ಮಾಡಿ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಬಳಸಲು, ಸೆಟ್ಟಿಂಗ್‌ಗಳು &gt; ಗೌಪ್ಯತೆ ಎಂಬಲ್ಲಿ "<b>"ಕ್ಯಾಮರಾ ಪ್ರವೇಶವನ್ನು"</b>" ಆನ್ ಮಾಡಿ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಹೆಚ್ಚಿನ ಮಾರ್ಗಗಳನ್ನು ಹೊಂದಿಸಿ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ಫಿಂಗರ್‌ ಪ್ರಿಂಟ್ ಸೇರಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್‌ಲಾಕ್"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> ಸಿದ್ಧಪಡಿಸಲಾಗುತ್ತಿದೆ."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ಬೂಟ್ ಪೂರ್ಣಗೊಳಿಸಲಾಗುತ್ತಿದೆ."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಬೇಕೇ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ಫಿಂಗರ್‌ ಪ್ರಿಂಟ್ ಅನ್ನು ಸೆಟ್ಟಪ್ ಮಾಡುವಾಗ ನೀವು ಪವರ್ ಬಟನ್‌ಅನ್ನು ಒತ್ತಿದ್ದೀರಿ \n\nಸಾಮಾನ್ಯವಾಗಿ ಇದರಿಂದ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಆಫ್ ಆಗುತ್ತದೆ."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ಆಫ್ ಮಾಡಿ"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ರದ್ದುಗೊಳಿಸಿ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ಸೆಟಪ್ ಮುಂದುವರಿಸಬೇಕೆ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ನೀವು ಪವರ್ ಬಟನ್ ಒತ್ತಿದ್ದೀರಿ — ಇದು ಸಾಮಾನ್ಯವಾಗಿ ಸ್ಕ್ರೀನ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ.\n\nನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಹೊಂದಿಸುವಾಗ ಲಘುವಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಿ"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ಸೆಟಪ್ ಮುಂದುವರಿಸಿ"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಪರಿಶೀಲನೆ ಮುಂದುವರಿಸುವುದೇ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ನೀವು ಪವರ್ ಬಟನ್ ಒತ್ತಿದ್ದೀರಿ — ಇದು ಸಾಮಾನ್ಯವಾಗಿ ಸ್ಕ್ರೀನ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ.\n\nನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಪರಿಶೀಲಿಸಲು ಲಘುವಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಿ"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ಮುಂದುವರಿಸಿ"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ರನ್ ಆಗುತ್ತಿದೆ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ಆಟಕ್ಕೆ ಹಿಂತಿರುಗಲು ಟ್ಯಾಪ್‌ ಮಾಡಿ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ಆಟವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index aacc58bf0c26..01d040fa41a8 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"앱이 IMS 서비스를 사용하여 자동으로 전화를 걸 수 있도록 허용합니다."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"휴대전화 상태 및 ID 읽기"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"앱이 기기의 휴대전화 기능에 접근할 수 있도록 허용합니다. 이 권한을 사용하면 앱이 전화번호 및 기기의 ID, 활성 통화인지 여부, 통화가 연결된 원격 번호 등을 확인할 수 있습니다."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"기본 전화 상태 및 ID 읽기"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"앱이 기기의 기본 전화 기능에 액세스할 수 있도록 허용합니다."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"시스템을 통해 통화 연결"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"통화 환경을 개선하기 위해 앱이 시스템을 통해 통화를 연결하도록 허용합니다."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"시스템을 통해 통화 확인 및 제어"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"탭하여 얼굴 모델을 삭제한 후 다시 얼굴을 추가하세요"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"얼굴 인식 잠금 해제 설정"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"휴대전화의 화면을 응시하여 잠금 해제할 수 있습니다."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"얼굴 인식 잠금 해제를 사용하려면 설정 &gt; 개인 정보 보호에서 "<b>"카메라 액세스"</b>"를 사용 설정하세요."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"다른 잠금 해제 방법 설정"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"지문을 추가하려면 탭하세요."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"지문 잠금 해제"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> 준비 중..."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"앱을 시작하는 중입니다."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"부팅 완료"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"화면을 끄시겠습니까?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"지문을 설정하는 중에 전원 버튼이 눌렸습니다.\n\n이렇게 하면 보통 화면이 꺼집니다."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"끄기"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"취소"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"설정을 계속하시겠어요?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"전원 버튼을 눌렀습니다. 이러면 보통 화면이 꺼집니다.\n\n지문 설정 중에 가볍게 탭하세요."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"화면 끄기"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"설정 계속"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"지문 인증을 계속할까요?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"전원 버튼을 눌렀습니다. 이러면 보통 화면이 꺼집니다.\n\n지문을 인식하려면 화면을 가볍게 탭하세요."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"화면 끄기"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"계속"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> 실행 중"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"게임으로 돌아가려면 탭하세요."</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"게임 선택"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 5399b4c4b733..e15377344230 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Колдонмого сизди катыштырбай туруп, IMS кызматынын жардамы менен, чалууларды жасоо мүмкүнчүлүгүн берет."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"телефондун абалын жана аныктыгын окуу"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Колдонмого түзмөктүн чалуу мүмкүнчүлүктөрүнө жетки алуу уруксатын берет. Бул уруксат колдонмого, телефондун номурун, түзмөктүн ID-син, чалуунун абалын жана байланышта чыккан номурду аныктоого жол берет."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"негизги телефония статусун окуу жана аныктоо"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Колдонмого түзмөктүн негизги телефония функцияларын колдонууга мүмкүнчүлүк берет."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"чалууларды тутум аркылуу өткөрүү"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Чалуунун сапатын жакшыртуу максатында колдонмого чалууларын тутум аркылуу өткөрүүгө уруксат берет."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"тутум аркылуу чалууларды көрүп, көзөмөлдөө."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Жүзүңүздүн үлгүсүн өчүрүү үчүн басып, жаңы үлгүнү кошуңуз"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Жүзүнөн таанып ачууну жөндөө"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Телефонуңузду карап туруп эле кулпусун ачып алыңыз"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Жүзүнөн таанып ачуу функциясын колдонуу үчүн Жөндөөлөр &gt; Купуялык бөлүмүнө өтүп, "<b>"Камераны колдонууну"</b>" күйгүзүңүз"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Кулпусун ачуунун көбүрөөк жолдорун жөндөңүз"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Манжа изин кошуу үчүн басыңыз"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Кулпуланган түзмөктү манжа изи менен ачуу"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> даярдалууда."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Колдонмолорду иштетип баштоо"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Жүктөлүүдө"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Экран өчүрүлсүнбү?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Манжаңыздын изин жөндөп жатканда күйгүзүү/өчүрүү баскычын басып алдыңыз.\n\nБул адатта экранды өчүрөт."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Өчүрүү"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Жок"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Жөндөөнү улантасызбы?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Кубат баскычын бастыңыз — адатта, бул экранды өчүрөт.\n\nМанжаңыздын изин жөндөп жатканда аны акырын басып көрүңүз."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Экранды өчүрүү"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Жөндөөнү улантуу"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Манжаңыздын изин ырастоону улантасызбы?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Кубат баскычын бастыңыз — адатта, бул экранды өчүрөт.\n\nМанжаңыздын изин ырастоо үчүн аны акырын басып көрүңүз."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Экранды өчүрүү"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Улантуу"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> иштеп жатат"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Оюнга кайтуу үчүн таптаңыз"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Оюн тандоо"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 88de60dd445c..b3cb5afea989 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"ອະ​ນຸ​ຍາດ​ໃຫ້​ແອັບ​ໃຊ້​ການ​ບໍ​ລິ​ການ IMS ເພື່ອ​ໂທ​ໂດຍ​ບໍ່​ມີ​ການ​ຊ່ວຍ​ເຫຼືອ​ຂອງ​ທ່ານ."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ອ່ານສະຖານະ ແລະຂໍ້ມູນລະບຸໂຕຕົນຂອງໂທລະສັບ"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ອະນຸຍາດໃຫ້ແອັບຯ ເຂົ້າເຖິງຄວາມສາມາດການໂທລະສັບຂອງອຸປະກອນ. ການກຳນົດສິດນີ້ເຮັດໃຫ້ແອັບຯສາມາດກວດສອບເບີໂທລະສັບ ແລະ ID ຂອງອຸປະກອນ, ບໍ່ວ່າການໂທຈະຍັງດຳເນີນຢູ່ ແລະເບີປາຍທາງເຊື່ອມຕໍ່ຢູ່ຫຼືບໍ່ກໍຕາມ."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ອ່ານສະຖານະໂທລະສັບພື້ນຖານ ແລະ ຕົວຕົນ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ອະນຸຍາດໃຫ້ແອັບເຂົ້າເຖິງຄຸນສົມບັດໂທລະສັບພື້ນຖານຂອງອຸປະກອນໄດ້."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ເຫັນ ແລະ ຄວບຄຸມການໂທຜ່ານລະບົບ."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ແຕະເພື່ອລຶບຮູບແບບໃບໜ້າຂອງທ່ານ, ຈາກນັ້ນເພີ່ມໃບໜ້າຂອງທ່ານໃສ່ໃໝ່"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ຕັ້ງຄ່າການປົດລັອກດ້ວຍໜ້າ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ປົດລັອກໂທລະສັບຂອງທ່ານໂດຍການເບິ່ງມັນ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ເພື່ອໃຊ້ການປົດລັອກດ້ວຍໜ້າ, ກະລຸນາເປີດໃຊ້ "<b>"ສິດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບ"</b>" ໃນການຕັ້ງຄ່າ &gt; ຄວາມເປັນສ່ວນຕົວ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ຕັ້ງຄ່າວິທີເພີ່ມເຕີມເພື່ອປົດລັອກ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ແຕະເພື່ອເພີ່ມລາຍນິ້ວມື"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ປົດລັອກດ້ວຍລາຍນິ້ວມື"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"ກຳ​ລັງ​ກຽມ <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ກຳລັງເປີດແອັບຯ."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ກຳລັງສຳເລັດການເປີດລະບົບ."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ປິດໜ້າຈໍໄວ້ບໍ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ໃນລະຫວ່າງການຕັ້ງຄ່າລາຍນິ້ວມືຂອງທ່ານ, ທ່ານກົດປຸ່ມເປີດປິດ.\n\nໂດຍປົກກະຕິນີ້ຈະເປັນການປິດໜ້າຈໍຂອງທ່ານ."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ປິດໄວ້"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ຍົກເລີກ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ສືບຕໍ່ການຕັ້ງຄ່າບໍ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ທ່ານກົດປຸ່ມເປີດປິດ, ປົກກະຕິນີ້ຈະປິດໜ້າຈໍ.\n\nລອງແຕະຄ່ອຍໆໃນລະຫວ່າງຕັ້ງຄ່າລາຍນິ້ວມືຂອງທ່ານ."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ປິດໜ້າຈໍ"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ສືບຕໍ່ການຕັ້ງຄ່າ"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ສືບຕໍ່ການຢັ້ງຢືນລາຍນິ້ວມືຂອງທ່ານບໍ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ທ່ານກົດປຸ່ມເປີດປິດ, ປົກກະຕິນີ້ຈະເປັນການປິດໜ້າຈໍ.\n\nໃຫ້ລອງແຕະຄ່ອຍໆເພື່ອຢັ້ງຢືນລາຍນິ້ວມືຂອງທ່ານ."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ປິດໜ້າຈໍ"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ສືບຕໍ່"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ກຳລັງເຮັດວຽກ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tap to return to game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choose game"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 180e1022c8ed..70f0f424cb7a 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Programai leidžiama naudoti IMS paslaugą, kad būtų galima atlikti skambučius be jūsų įsikišimo."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"skaityti telefono būseną ir tapatybę"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Leidžiama programai pasiekti telefono funkcijas įrenginyje. Šis leidimas suteikia teisę programai nustatyti telefono numerį ir įrenginio ID, tai, ar skambutis aktyvus, ir skambučiu prijungtą nuotolinį numerį."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"skaityti pagrindinę telefonijos būsenos ir tapatybės informaciją"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Programai leidžiama pasiekti pagrindines įrenginio telefonijos funkcijas."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"nukreipti skambučius per sistemą"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Programai leidžiama nukreipti jos skambučius per sistemą siekiant pagerinti skambinimo paslaugas."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"matyti ir valdyti skambučius per sistemą."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Palieskite, kad ištrintumėte veido modelį, tada iš naujo pridėkite veidą"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Atrakinimo pagal veidą nustatymas"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Atrakinkite telefoną pažiūrėję į jį"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Jei norite naudoti atrakinimą pagal veidą, įjunkite parinktį "<b>"Prieiga prie fotoaparato"</b>" skiltyje „Nustatymai“ &gt; „Privatumas“"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Daugiau atrakinimo metodų nustatymas"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Palieskite, kad pridėtumėte kontrolinį kodą"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Atrakinimas kontroliniu kodu"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Ruošiama „<xliff:g id="APPNAME">%1$s</xliff:g>“."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Paleidžiamos programos."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Užbaigiamas paleidimas."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Išjungti ekraną?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Nustatydami kontrolinį kodą paspaudėte maitinimo mygtuką.\n\nĮprastai juo išjungiamas ekranas."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Išjungti"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Atšaukti"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Tęsti sąranką?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Paspaudėte maitinimo mygtuką, taip paprastai išjungiamas ekranas.\n\nNustatydami kontrolinį kodą, pabandykite jį švelniai paliesti."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Išjungti ekraną"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Tęsti sąranką"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Tęsti kontrolinio kodo patvirtinimą?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Paspaudėte maitinimo mygtuką, taip paprastai išjungiamas ekranas.\n\nNorėdami patvirtinti kontrolinį kodą, pabandykite jį švelniai paliesti."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Išjungti ekraną"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Tęsti"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Vykdoma „<xliff:g id="APP">%1$s</xliff:g>“"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Palieskite, kad grįžtumėte į žaidimą"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pasirinkite žaidimą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 8f3e989a31ce..1df77747f878 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -221,7 +221,7 @@
<string name="turn_on_radio" msgid="2961717788170634233">"Ieslēgt bezvadu tīklu"</string>
<string name="turn_off_radio" msgid="7222573978109933360">"Izslēgt bezvadu tīklu"</string>
<string name="screen_lock" msgid="2072642720826409809">"Bloķēt ekrānu"</string>
- <string name="power_off" msgid="4111692782492232778">"Strāvas padeve ir izslēgta."</string>
+ <string name="power_off" msgid="4111692782492232778">"Izslēgt tālruni"</string>
<string name="silent_mode_silent" msgid="5079789070221150912">"Zvanītājs izslēgts"</string>
<string name="silent_mode_vibrate" msgid="8821830448369552678">"Zvanītājs vibrācijas režīmā"</string>
<string name="silent_mode_ring" msgid="6039011004781526678">"Zvanītājs ieslēgts"</string>
@@ -245,7 +245,7 @@
<string name="global_actions" product="tv" msgid="3871763739487450369">"Android TV opcijas"</string>
<string name="global_actions" product="default" msgid="6410072189971495460">"Tālruņa opcijas"</string>
<string name="global_action_lock" msgid="6949357274257655383">"Ekrāna bloķētājs"</string>
- <string name="global_action_power_off" msgid="4404936470711393203">"Strāvas padeve izslēgta."</string>
+ <string name="global_action_power_off" msgid="4404936470711393203">"Izslēgt strāvas padevi"</string>
<string name="global_action_power_options" msgid="1185286119330160073">"Barošana"</string>
<string name="global_action_restart" msgid="4678451019561687074">"Restartēt"</string>
<string name="global_action_emergency" msgid="1387617624177105088">"Ārkārtas situācija"</string>
@@ -474,6 +474,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Ļauj lietotnei izmantot tūlītējās ziņojumapmaiņas pakalpojumu, lai veiktu zvanus bez jūsu ziņas."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lasīt tālruņa statusu un identitāti"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Ļauj lietotnei piekļūt ierīces tālruņa funkcijām. Ar šo atļauju lietotne var noteikt tālruņa numuru un ierīču ID, zvana statusu un attālo numuru, ar ko ir izveidots savienojums, veicot zvanu."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lasīt telefonijas statusa un identitātes pamatinformāciju"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Lietotnei tiek atļauta piekļuve telefonijas pamatfunkcijām ierīcē."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"maršrutēt zvanus sistēmā"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Ļauj lietotnei maršrutēt tās zvanus sistēmā, lai uzlabotu zvanīšanas pieredzi."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"skatīt un kontrolēt zvanus sistēmā."</string>
@@ -625,6 +627,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Pieskarieties, lai izdzēstu sejas modeli, un pēc tam vēlreiz pievienojiet seju"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Autorizācijas pēc sejas iestatīšana"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Atbloķējiet tālruni, skatoties uz to"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Lai izmantotu autorizāciju pēc sejas, sadaļā Iestatījumi &gt; Konfidencialitāte ieslēdziet opciju "<b>"Piekļuve kamerai"</b>"."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Citi atbloķēšanas veidi"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Pieskarieties, lai pievienotu pirksta nospiedumu"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Autorizācija ar pirksta nospiedumu"</string>
@@ -1296,10 +1299,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Notiek lietotnes <xliff:g id="APPNAME">%1$s</xliff:g> sagatavošana."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Notiek lietotņu palaišana."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Tiek pabeigta sāknēšana."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vai izslēgt ekrānu?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Iestatot pirksta nospiedumu, jūs nospiedāt barošanas pogu.\n\nTādējādi parasti tiek izslēgts ekrāns."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Izslēgt"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Atcelt"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Vai turpināt iestatīšanu?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Jūs nospiedāt barošanas pogu — tādējādi parasti tiek izslēgts ekrāns.\n\nMēģiniet viegli pieskarties, iestatot pirksta nospiedumu."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Izslēgt ekrānu"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Turpināt iestatīšanu"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vai apstiprināt pirksta nospiedumu?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Jūs nospiedāt barošanas pogu — tādējādi parasti tiek izslēgts ekrāns.\n\nMēģiniet viegli pieskarties, lai apstiprinātu pirksta nospiedumu."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Izslēgt ekrānu"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Turpināt"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> darbojas"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Pieskarieties, lai atgrieztos spēlē"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Spēles izvēlēšanās"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 9717112d2b81..cc86005f4698 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Дозволува апликацијата да ја користи услугата IMS за повици без ваша интервенција."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"прочитај ги статусот и идентитетот на телефонот"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Овозможува апликацијата да пристапи кон карактеристиките на телефонот на уредот. Оваа дозвола овозможува апликацијата да ги утврди телефонскиот број и ID на уредот, дали повикот е активен и далечинскиот број поврзан со повикот."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"читање основен телефонски статус и идентитет"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Дозволете апликацијава да пристапи до основните телефонски функции на уредот."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"пренасочи повици преку системот"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Дозволете ѝ на апликацијата да ги пренасочи повиците преку системот за да го подобри искуството при јавувањето."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"гледање и контролирање повици преку системот."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Допрете за да го избришете вашиот модел на лице, а потоа повторно додајте го лицето"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Поставете „Отклучување со лик“"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Отклучете го телефонот со гледање во него"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"За да користите „Отклучување со лик“, вклучете "<b>"Пристап до камерата"</b>" во „Поставки &gt; Приватност“"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Поставете уште начини за отклучување"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Допрете за да додадете отпечаток"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Отклучување со отпечаток на прст"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Се подготвува <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Се стартуваат апликациите."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Подигањето завршува."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Да се исклучи екранот?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Додека го поставувавте вашиот отпечаток, го притиснавте копчето за вклучување.\n\nОва обично го исклучува екранот."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Исклучи"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Откажи"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Дали да продолжи поставувањето?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Го притиснавте копчето за вклучување — така обично се исклучува екранот.\n\nДопрете лесно додека го поставувате отпечатокот."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Исклучи го екранот"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Продолжи"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Да продолжи потврдувањето на отпечаток?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Го притиснавте копчето за вклучување — така обично се исклучува екранот.\n\nДопрете лесно за да го потврдите отпечатокот."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Исклучи го екранот"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Продолжи"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> работи"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Допрете за да се вратите во играта"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Избор на игра"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 12eb2a2e0ddd..251770422101 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"നിങ്ങളുടെ ഇടപെടൽ ഇല്ലാതെ കോളുകൾ ചെയ്യാൻ IMS സേവനം ഉപയോഗിക്കുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ഫോൺ നിലയും ഐഡന്റിറ്റിയും റീഡുചെയ്യുക"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ഉപകരണത്തിന്റെ ഫോൺ സവിശേഷതകൾ ആക്‌സസ്സുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഈ അനുമതി ഫോൺ നമ്പർ, ഉപകരണ ഐഡികൾ, ഒരു കോൾ സജീവമാണോയെന്നത്, ഒരു കോൾ കണക്റ്റുചെയ്‌ത വിദൂര നമ്പർ എന്നിവ നിർണ്ണയിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ടെലിഫോണിന്റെ അടിസ്ഥാന നിലയും ഐഡന്റിറ്റിയും വായിക്കാൻ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ഉപകരണത്തിലെ ടെലിഫോൺ സംബന്ധമായ അടിസ്ഥാന ഫീച്ചറുകൾ ആക്‌സസ് ചെയ്യാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"കോളുകൾ സിസ്റ്റത്തിലൂടെ വിടുക"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"കോളിംഗ് അനുഭവം ‌മെച്ചപ്പെടുത്തുന്നതിനായി തങ്ങളുടെ ‌കോളുകൾ സിസ്റ്റത്തിലേയ്ക്ക് വഴിതിരിച്ചുവിടാൻ ആപ്പുകളെ അനുവദിക്കുന്നു."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"സിസ്‌റ്റത്തിലൂടെ കോളുകൾ കാണുകയും നിയന്ത്രിക്കുകയും ചെയ്യുക."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"നിങ്ങളുടെ മുഖ മോഡൽ ഇല്ലാതാക്കാൻ ടാപ്പ് ചെയ്യുക, തുടർന്ന് അത് വീണ്ടും ചേർക്കുക"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ഫെയ്‌സ് അൺലോക്ക് സജ്ജീകരിക്കുക"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ഫോണിലേക്ക് നോക്കി അത് അൺലോക്ക് ചെയ്യുക"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ഫെയ്‌സ് അൺലോക്ക് ഉപയോഗിക്കാൻ, ക്രമീകരണം &gt; സ്വകാര്യത എന്നതിൽ "<b>"ക്യാമറാ ആക്‌സസ്"</b>" ഓണാക്കുക"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"അൺലോക്ക് ചെയ്യുന്നതിനുള്ള കൂടുതൽ വഴികൾ സജ്ജീകരിക്കുക"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ഫിംഗർപ്രിന്റ് ചേർക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ഫിംഗർപ്രിന്റ് അൺലോക്ക്"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> തയ്യാറാക്കുന്നു."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"അപ്ലിക്കേഷനുകൾ ആരംഭിക്കുന്നു."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ബൂട്ട് ചെയ്യൽ പൂർത്തിയാകുന്നു."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"സ്‌ക്രീൻ ഓഫാക്കണോ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ഫിംഗർപ്രിന്റ് സജ്ജീകരിച്ച് കൊണ്ടിരുന്നപ്പോൾ നിങ്ങൾ പവർ ബട്ടൺ അമർത്തി.\n\nഇത് സാധാരണയായി സ്ക്രീൻ ഓഫാകുന്നതിന് കാരണമാകും."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ഓഫാക്കുക"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"റദ്ദാക്കുക"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"സജ്ജീകരണം തുടരണോ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"നിങ്ങൾ പവർ ബട്ടൺ അമർത്തി — സാധാരണയായി ഇത് സ്ക്രീൻ ഓഫാകുന്നതിന് കാരണമാകും.\n\nനിങ്ങളുടെ ഫിംഗർപ്രിന്റ് സജ്ജീകരിക്കുമ്പോൾ മൃദുവായി ടാപ്പ് ചെയ്ത് നോക്കുക."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"സ്ക്രീൻ ഓഫാക്കുക"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"സജ്ജീകരണം തുടരുക"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിക്കൽ തുടരണോ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"നിങ്ങൾ പവർ ബട്ടൺ അമർത്തി — സാധാരണയായി ഇത് സ്ക്രീൻ ഓഫാകുന്നതിന് കാരണമാകും.\n\nനിങ്ങളുടെ ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിക്കാൻ മൃദുവായി ടാപ്പ് ചെയ്ത് നോക്കുക."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"സ്ക്രീൻ ഓഫാക്കുക"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"തുടരുക"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> പ്രവർത്തിക്കുന്നു"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ഗെയിമിലേക്ക് മടങ്ങാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ഗെയിം തിരഞ്ഞെടുക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 68fdb072cfea..6be8243e8347 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Апп нь дуудлага хийхдээ таны оролцоогүйгээр IMS үйлчилгээг ашиглах боломжтой."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"утасны статус ба таниулбарыг унших"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Апп нь төхөөрөмжийн утасны функцд хандах боломжтой. Энэ зөвшөөрөл нь апп-д утасны дугаар болон төхөөрөмжийн ID-г, дуудлага идэвхтэй эсэх, холын дугаар дуудлагаар холбогдсон байгаа эсэхийг тогтоох боломжийг олгоно,"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"утасны үндсэн статус болон таних мэдээллийг уншуулна уу"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Аппад төхөөрөмжийн утасны үндсэн онцлогт хандах боломжийг олгоно."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"системээр дамжуулах дуудлага"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Дуудлагыг сайжруулахын тулд дуудлагаа системээр дамжуулах зөвшөөрлийг апп-д олгодог."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"систем дэх дуудлагыг харах болон хянах."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Нүүрний загвараа устгахын тулд товшоод, дараа нь царайгаа дахин нэмнэ үү"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Царайгаар түгжээ тайлахыг тохируулах"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Утас руугаа харж түгжээг нь тайлна уу"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Царайгаар түгжээ тайлахыг ашиглахын тулд Тохиргоо &gt; Нууцлал хэсэгт "<b>" Камерын хандалтыг "</b>" асаана уу"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Түгжээ тайлах илүү олон арга тохируулна уу"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Хурууны хээ нэмэхийн тулд товшино уу"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Хурууны хээгээр түгжээ тайлах"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Бэлдэж байна <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Апп-г эхлүүлж байна."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Эхлэлийг дуусгаж байна."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Дэлгэцийг унтраах уу?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Та хурууны хээгээ тохируулж байх үед Асаах/унтраах товчийг дарсан байна.\n\nЭнэ нь ихэвчлэн таны дэлгэцийг унтраана."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Унтраах"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Цуцлах"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Тохируулгыг үргэлжлүүлэх үү?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Та асаах/унтраах товчийг дарсан байна — энэ нь ихэвчлэн дэлгэцийг унтраадаг.\n\nХурууны хээгээ тохируулж байх үедээ зөөлөн товшиж үзнэ үү."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Дэлгэцийг унтраах"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Тохируулгыг үргэлжлүүлэх"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Хурууны хээгээ үргэлжлүүлэн баталгаажуулах уу?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Та асаах/унтраах товчийг дарсан байна — энэ нь ихэвчлэн дэлгэцийг унтраадаг.\n\nХурууны хээгээ баталгаажуулахын тулд зөөлөн товшиж үзнэ үү."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Дэлгэцийг унтраах"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Үргэлжлүүлэх"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ажиллаж байна"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Тоглоом руу буцахын тулд товших"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Тоглоом сонгох"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index bf9143d87ec0..69279ee78263 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"आपल्‍या हस्तक्षेपाशिवाय अ‍ॅपला कॉल करण्‍यासाठी IMS सेवा वापरण्याची अनुमती देते."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"फोन स्थिती आणि ओळख वाचा"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"डिव्हाइस च्या फोन वैशिष्ट्यांवर अ‍ॅक्सेस करण्यास ॲपला अनुमती देते. ही परवानगी कॉल ॲक्टिव्हेट असला किंवा नसला तरीही, फोन नंबर आणि डिव्हाइस आयडी आणि कॉलद्वारे कनेक्ट केलेला रिमोट नंबर निर्धारित करण्यासाठी ॲपला अनुमती देते."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"टेलिफोनी सुविधांशी संबंधित मूलभूत स्टेटस आणि ओळखीशी संबंधित माहिती वाचा"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"यामुळे ॲपला डिव्हाइसची मूलभूत टेलिफोनी वैशिष्ट्ये अ‍ॅक्सेस करण्याची अनुमती मिळते."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"प्रणालीच्या माध्यमातून कॉल रूट करा"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"कॉल करण्याचा अनुभव सुधारण्यासाठी ॲपला त्याचे कॉल प्रणालीच्या माध्यमातून रूट करू देते."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"सिस्टम वापरून कॉल पहा आणि नियंत्रण ठेवा."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"फेस मॉडेल हटवण्यासाठी टॅप करा, त्यानंतर तुमचा चेहरा पुन्हा जोडा"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"फेस अनलॉक सेट करा"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"तुमच्या फोनकडे पाहून तो अनलॉक करा"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"फेस अनलॉक वापरण्यासाठी, सेटिंग्ज &gt; गोपनीयता येथे "<b>"कॅमेरा अ‍ॅक्सेस"</b>" सुरू करा"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"अनलॉक करण्याच्या आणखी पद्धती सेट करा"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"फिंगरप्रिंट जोडण्यासाठी टॅप करा"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"फिंगरप्रिंट अनलॉक"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> तयार करत आहे."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"अ‍ॅप्स सुरू करत आहे."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"बूट समाप्त होत आहे."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"स्क्रीन बंद करायची आहे का?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"तुम्ही तुमची फिंगरप्रिंट सेट करत असताना पॉवर बटण दाबले.\n\nयामुळे सहसा तुमची स्क्रीन बंद होते."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"बंद करा"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"रद्द करा"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"सेट करणे पुढे सुरू ठेवायचे का?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"तुम्ही पॉवर बटण दाबले — हे सहसा स्क्रीन बंद करते.\n\nतुमचे फिंगरप्रिंट सेट करताना हलके टॅप करून पहा."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"स्क्रीन बंद करा"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"सेट करणे सुरू ठेवा"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"फिंगरप्रिंट पडताळणी सुरू ठेवायची का?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"तुम्ही पॉवर बटण दाबले — हे सहसा स्क्रीन बंद करते.\n\nतुमच्या फिंगरप्रिंटची पडताळणी करण्यासाठी हलके टॅप करून पहा."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"स्क्रीन बंद करा"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"पुढे सुरू ठेवा"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"रन होणारे <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"गेमवर परत जाण्यासाठी टॅप करा"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"गेम निवडा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 05395c91fea7..27aac1ac2a74 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Membenarkan apl menggunakan perkhidmatan IMS untuk membuat panggilan tanpa campur tangan anda."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"baca status dan identiti telefon"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Membenarkan apl mengakses ciri telefon pada peranti. Kebenaran ini membolehkan apl menentukan nombor telefon dan ID peranti, sama ada panggilan aktif dan nombor jauh yang dihubungkan dengan panggilan."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"membaca status telefoni asas dan identiti"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Membenarkan apl mengakses ciri telefoni asas peranti."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"halakan panggilan menerusi sistem"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Membenarkan apl menghalakan panggilan menerusi sistem untuk meningkatkan pengalaman panggilan."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"lihat dan kawal panggilan melalui sistem."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ketik untuk memadamkan model wajah anda, kemudian tambahkan wajah anda semula"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Sediakan Buka Kunci Wajah"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Buka kunci telefon anda dengan melihat telefon anda"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Untuk menggunakan Buka Kunci Wajah, hidupkan "<b>"akses Kamera"</b>" dalam Tetapan &gt; Privasi"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Sediakan lebih banyak cara untuk membuka kunci"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ketik untuk menambahkan cap jari"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Buka Kunci Cap Jari"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Menyediakan <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Memulakan apl."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"But akhir."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Matikan skrin?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Semasa menyediakan cap jari anda, anda menekan butang Kuasa.\n\nTindakan ini biasanya mematikan skrin anda."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Matikan"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Batal"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Teruskan persediaan?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Anda menekan butang kuasa — tindakan ini biasanya mematikan skrin.\n\nCuba ketik dengan perlahan semasa menetapkan cap jari anda."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Matikan skrin"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Teruskan persediaan"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Terus mengesahkan cap jari anda?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Anda menekan butang kuasa — tindakan ini biasanya mematikan skrin.\n\nCuba ketik dengan perlahan untuk mengesahkan cap jari anda."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Matikan skrin"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Teruskan"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> dijalankan"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ketik untuk kembali ke permainan"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pilih permainan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 4f97ef9b4da0..eefbe153d6ba 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"သင့်ရဲ့ဝင်ရောက်စွက်ဖက်မှုမပါဘဲ IMS ဝန်ဆောင်မှုကိုအသုံးပြုပြီး ဖုန်းခေါ်ဆိုနိုင်ရန် အပ်ဖ်ကို ခွင့်ပြုထားပါ။"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ဖုန်းရဲ့ အခြေအနေ နှင့် အမှတ်သညာအား ဖတ်ခြင်း"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"အပလီကေးရှင်းအား ဖုန်းရဲ့ စွမ်းဆောင်ချက်များအား သုံးခွင့်ပြုပါ။ အပလီကေးရှင်းအနေဖြင့် ဖုန်းနံပါတ်၊ စက်နံပါတ်၊ ဖုန်းခေါ်နေမှု ရှိမရှိနှင့် တဖက်မှ ဖုန်းနံပါတ် များအား သိရှိနိုင်ပါသည်"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"အခြေခံတယ်လီဖုန်းအခြေအနေနှင့် အထောက်အထားကို ဖတ်ခြင်း"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"စက်၏ အခြေခံတယ်လီဖုန်းဝန်ဆောင်မှုများသုံးရန် အက်ပ်ကို ခွင့်ပြုသည်။"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ခေါ်ဆိုမှုများကို စနစ်မှတစ်ဆင့် ဖြတ်သန်းခွင့်ပြုပါ"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"ခေါ်ဆိုမှု အတွေ့အကြုံ ပိုမိုကောင်းမွန်လာစေရန်အတွက် အက်ပ်၏ ခေါ်ဆိုမှုအား စနစ်မှတစ်ဆင့် ဖြတ်သန်းရန် ခွင့်ပြုပါသည်။"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"စနစ်မှတစ်ဆင့် ခေါ်ဆိုမှုများကို ကြည့်ရှုထိန်းချုပ်ပါ။"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"သင်၏မျက်နှာနမူနာကို ဖျက်ရန် တို့ပါ။ ထို့နောက် သင့်မျက်နှာကို ထပ်ထည့်ပါ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို ထည့်သွင်းပါ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"သင့်ဖုန်းကိုကြည့်၍ သော့ဖွင့်ပါ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို သုံးရန် "<b>"ကင်မရာ သုံးခွင့်"</b>" ကို ‘ဆက်တင်များ &gt; ကန့်သတ်ဆက်တင်’ တွင်ဖွင့်ပါ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"သော့ဖွင့်ရန် နောက်ထပ်နည်းလမ်းများကို စနစ်ထည့်သွင်းပါ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"လက်ဗွေထည့်ရန် တို့ပါ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"လက်ဗွေသုံး လော့ခ်ဖွင့်ခြင်း"</string>
@@ -1172,8 +1175,8 @@
<string name="delete" msgid="1514113991712129054">"ဖျက်ရန်"</string>
<string name="copyUrl" msgid="6229645005987260230">"URLအား ကူးခြင်း"</string>
<string name="selectTextMode" msgid="3225108910999318778">"စာသား ရွေးရန်"</string>
- <string name="undo" msgid="3175318090002654673">"တစ်ဆင့်နောက်ပြန်ရန်"</string>
- <string name="redo" msgid="7231448494008532233">"ထပ်လုပ်ပါ"</string>
+ <string name="undo" msgid="3175318090002654673">"နောက်ပြန်ရန်"</string>
+ <string name="redo" msgid="7231448494008532233">"ပြန်လုပ်ရန်"</string>
<string name="autofill" msgid="511224882647795296">"အော်တိုဖြည့်"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"စာတိုရွေးချယ်မှု"</string>
<string name="addToDictionary" msgid="8041821113480950096">"အဘိဓာန်ထဲ ထည့်ပါ"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> အားပြင်ဆင်နေသည်။"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"အက်ပ်များကို စတင်နေ"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"လုပ်ငန်းစနစ်ထည့်သွင်း၍ ပြန်လည်စတင်ရန် ပြီးပါပြီ"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ဖန်သားပြင်ကို ပိတ်မလား။"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"သင့်လက်ဗွေကို စနစ်ထည့်သွင်းနေစဉ် ဖွင့်ပိတ်ခလုတ်ကို ဖိထားပါ။\n\n၎င်းက အများအားဖြင့် သင့်ဖန်သားပြင်ကို ပိတ်ပါသည်။"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ပိတ်ရန်"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"မလုပ်တော့"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ဆက်လက်၍ စနစ်ထည့်သွင်းလိုပါသလား။"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ဖွင့်ပိတ်ခလုတ်ကို သင်နှိပ်ခဲ့သည် — ၎င်းက ပုံမှန်အားဖြင့် စခရင်ကို ပိတ်စေသည်။\n\nသင့်လက်ဗွေကို ထည့်သွင်းသောအခါ ဖွဖွတို့ကြည့်ပါ။"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"စခရင် ပိတ်ရန်"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"စနစ်ဆက်ထည့်သွင်းရန်"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"သင့်လက်ဗွေကို ဆက်၍ အတည်ပြုမလား။"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ဖွင့်ပိတ်ခလုတ်ကို သင်နှိပ်ခဲ့သည် — ၎င်းက ပုံမှန်အားဖြင့် စခရင်ကို ပိတ်စေသည်။\n\nသင့်လက်ဗွေကို အတည်ပြုရန် ဖွဖွတို့ကြည့်ပါ။"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"စခရင် ပိတ်ရန်"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ရှေ့ဆက်ရန်"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> က အလုပ်လုပ်နေသည်"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ဂိမ်းသို့ ပြန်သွားရန် တို့ပါ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ဂိမ်းကို ရွေးခြင်း"</string>
@@ -1549,7 +1556,7 @@
<string name="sync_too_many_deletes" msgid="6999440774578705300">"ပယ်ဖျက်မည့်ကန့်သတ်နှုန်းကျော်လွန်သည်"</string>
<string name="sync_too_many_deletes_desc" msgid="7409327940303504440">"<xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>၊ account <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> အတွက် စုစုပေါင်း <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> အရာဖျက်ထားပါသည်။ သင်ဘာလုပ်ချင်ပါလဲ?"</string>
<string name="sync_really_delete" msgid="5657871730315579051">"ဤအရာများကိုဖျက်ပါ"</string>
- <string name="sync_undo_deletes" msgid="5786033331266418896">"ဖျက်ပီးသည်များကို ပယ်ဖျက်ရန်"</string>
+ <string name="sync_undo_deletes" msgid="5786033331266418896">"အားလုံးကို မဖျက်တော့ရန်"</string>
<string name="sync_do_nothing" msgid="4528734662446469646">"လက်ရှိ ဘာမှမလုပ်ရန်"</string>
<string name="choose_account_label" msgid="5557833752759831548">"အကောင့် တစ်ခု ရွေးပါ"</string>
<string name="add_account_label" msgid="4067610644298737417">"အကောင့်တစ်ခုထည့်ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index ccf5e97ddfde..4d8d2b0534e4 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Lar appen bruke nettprattjenesten til å ringe uten at du gjør noe."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lese telefonstatus og -identitet"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Lar appen bruke enhetens telefonfunksjoner. Med denne tillatelsen kan appen finne telefonnummer og enhets-ID-er, registrere om en samtale pågår, og se det eksterne nummeret det opprettes en forbindelse med via oppringing."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lese grunnleggende telefonstatus og identitet"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gir appen tilgang til de grunnleggende telefonfunksjonene på enheten."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"send anrop gjennom systemet"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Lar appen sende anrop gjennom systemet for å forbedre anropsopplevelsen."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"se og kontrollere anrop i systemet."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Trykk for å slette ansiktsmodellen din, og legg deretter til ansiktet på nytt"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurer ansiktslås"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Lås opp telefonen ved å se på den"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"For å bruke ansiktslås, slå på "<b>"Kameratilgang"</b>" i Innstillinger &gt; Personvern"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurer flere måter å låse opp på"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Trykk for å legge til et fingeravtrykk"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Opplåsing med fingeravtrykk"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Forbereder <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starter apper."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Ferdigstiller oppstart."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vil du slå av skjermen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Da du konfigurerte fingeravtrykket, trykket du på av/på-knappen.\n\nDette slår vanligvis av skjermen."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Slå av"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Avbryt"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Vil du fortsette konfigureringen?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du har trykket på av/på-knappen – dette slår vanligvis av skjermen.\n\nPrøv å trykke lett mens du konfigurerer fingeravtrykket ditt."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Slå av skjermen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Fortsett konfig."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Fortsett bekreftelse av fingeravtrykket?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du har trykket på av/på-knappen – dette slår vanligvis av skjermen.\n\nPrøv å trykke lett for å bekrefte fingeravtrykket ditt."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Slå av skjermen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Fortsett"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> kjører"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Trykk for å gå tilbake til spillet"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Velg et spill"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 99e3375bac63..12c4464843eb 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"तपाईँको हस्तक्षेप बिना नै कल गर्न IMS सेवा प्रयोग गर्न एपलाई अनुमति दिन्छ।"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"फोन स्थिति र पहिचान पढ्नुहोस्"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"उपकरणको फोन विशेषताहरूको पहुँच गर्न एपलाई अनुमति दिन्छ। यस अनुमतिले फोन नम्बर र उपकरणको IDs, कल सक्षम छ कि छैन र कलद्वारा जोडिएको टाढाको नम्बर निर्धारण गर्न अनुमति दिन्छ।"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"टेलिफोन सेवाका आधारभूत स्थिति र त्यसको पहिचानसम्बन्धी जानकारी रिड गर्ने"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"तपाईंले यो अनुमति दिनुभयो भने यो एपले यस डिभाइसको टेलिफोन सेवाका आधारभूत सुविधाहरू प्रयोग गर्न सक्छ।"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"प्रणाली मार्फत कल गर्न दिनुहोस्‌"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"कल गर्दाको अनुभवलाई सुधार्न यस एपलाई प्रणाली मार्फत कलहरू गर्न अनुमति दिन्छ।"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"प्रणालीमार्फत कलहरू हेर्नुका साथै तिनीहरूलाई नियन्त्रण गर्नुहोस्‌।"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"आफ्नो फेस मोडेल मेटाउन ट्याप गर्नुहोस् अनि आफ्नो अनुहार फेरि दर्ता गर्नुहोस्"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"फेस अनलक सेटअप गर्नुहोस्"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"फोनमा हेरेकै भरमा फोन अनलक गर्नुहोस्"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"फेस अनलक प्रयोग गर्न \"सेटिङ तथा गोपनीयता\" मा गई "<b>"क्यामेरा प्रयोग गर्ने अनुमति"</b>" दिनुहोस्"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"अनलक गर्ने अन्य तरिकाहरू सेटअप गर्नुहोस्"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"फिंगरप्रिन्ट हाल्न ट्याप गर्नुहोस्"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"फिंगरप्रिन्ट अनलक"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> तयारी गर्दै।"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"सुरुवात एपहरू।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"बुट पुरा हुँदै।"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"स्क्रिन अफ गर्ने हो?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"आफ्नो फिंगरप्रिन्ट सेटअप गर्दा तपाईंले पावर बटन थिच्नुभयो।\n\nयसो गर्दा सामान्यतया तपाईंको स्क्रिन अफ हुन्छ।"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"अफ गर्नुहोस्"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"रद्द गर्नुहोस्"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"सेटअप गर्ने प्रक्रिया जारी राख्ने हो?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"तपाईंले पावर बटन थिच्नुभयो — सामान्यतया स्क्रिन अफ गर्न यो बटन थिच्ने गरिन्छ।\n\nफिंगरप्रिन्ट सेटअप भइन्जेल हल्का तरिकाले यो बटन ट्याप गर्नुहोस्।"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"स्क्रिन अफ गर्नुहोस्"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"सेटअप जारी राख्नुस्"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"फिंगरप्रिन्ट पुष्टि गर्ने क्रम जारी राख्ने हो?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"तपाईंले पावर बटन थिच्नुभयो — सामान्यतया स्क्रिन अफ गर्न यो बटन थिच्ने गरिन्छ।\n\nतपाईं आफ्नो फिंगरप्रिन्ट पुष्टि गर्न चाहनुहुन्छ भने हल्का तरिकाले यो बटन ट्याप गरी हेर्नुहोस्।"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"स्क्रिन अफ गर्नुहोस्"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"जारी राख्नुहोस्"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> चलिरहेको छ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"खेलमा फर्कन ट्याप गर्नुहोस्"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"खेल छनौट गर्नुहोस्‌"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index a1b45bfc3521..858ca009df05 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Hiermee kan de app de IMS-service gebruiken om te bellen zonder je tussenkomst."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"telefoonstatus en -identiteit lezen"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Hiermee kan de app toegang krijgen tot de telefoonfuncties van het apparaat, Met deze rechten kan de app het telefoonnummer en de apparaat-ID\'s bepalen, of een gesprek actief is, en het andere telefoonnummer waarmee wordt gebeld."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"basistelefoonstatus en -identiteit lezen"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Hiermee geef je de app toegang tot de basistelefoonfuncties van het apparaat."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"gesprekken doorschakelen via het systeem"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Hiermee kan de app de bijbehorende gesprekken doorschakelen via het systeem om de belfunctionaliteit te verbeteren."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"gesprekken via het systeem bekijken en beheren"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tik om je gezichtsmodel te verwijderen en voeg je gezicht opnieuw toe"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Ontgrendelen via gezichtsherkenning instellen"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ontgrendel je telefoon door ernaar te kijken"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Als je Ontgrendelen via gezichtsherkenning wilt gebruiken, zet je "<b>"Cameratoegang"</b>" aan via Instellingen &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Stel meer manieren in om te ontgrendelen"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tik om een vingerafdruk toe te voegen"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Ontgrendelen met vingerafdruk"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> voorbereiden."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Apps starten."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Opstarten afronden."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Scherm uitzetten?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Toen je je vingerafdruk instelde, heb je op de aan/uit-knop gedrukt.\n\nDaarmee wordt het scherm meestal uitgezet."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Uitzetten"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Annuleren"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Doorgaan met instellen?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Je hebt op de aan/uit-knop gedrukt. Zo zet je meestal het scherm uit.\n\nRaak de knop voorzichtig aan terwijl je je vingerafdruk instelt."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Scherm uitzetten"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Doorgaan met instellen"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Doorgaan met verificatie van je vingerafdruk?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Je hebt op de aan/uit-knop gedrukt. Zo zet je meestal het scherm uit.\n\nRaak de knop voorzichtig aan om je vingerafdruk te verifiëren."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Scherm uitzetten"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Doorgaan"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> wordt uitgevoerd"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tik om terug te keren naar de game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Game kiezen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index afe04419927e..52015c29027f 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"ଆପଣଙ୍କ ହସ୍ତକ୍ଷେପ ବିନା କଲ୍‍ କରିପାରିବା ପାଇଁ ଆପ୍‌କୁ IMS ସେବା ବ୍ୟବହାର କରିବାକୁ ଦିଏ।"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ଫୋନ୍‍ ସ୍ଥିତି ଓ ପରିଚୟ ପଢ଼ନ୍ତୁ"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ଆପ୍‌କୁ ଡିଭାଇସ୍‌ର ଫୋନ୍‌ ବୈଶିଷ୍ଟ୍ୟ ଆକ୍ସେସ୍‍ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏହି ଅନୁମତି ଆପ୍‌କୁ ଫୋନ୍‌ ନମ୍ବର୍ ଓ ଡିଭାଇସ୍‌ IDଗୁଡ଼ିକୁ ନିର୍ଦ୍ଧାରଣ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ, କଲ୍ ସକ୍ରିୟ ଥିଲେ ବି ଏବଂ କଲ୍ ଦ୍ୱାରା ସଂଯୋଗ ଥିବା ରିମୋଟ୍‌ ନମ୍ବର୍‌କୁ ମଧ୍ୟ।"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ବେସିକ ଟେଲିଫୋନି ସ୍ଥିତି ଏବଂ ପରିଚୟ ପଢ଼ନ୍ତୁ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ଡିଭାଇସର ବେସିକ ଟେଲିଫୋନି ଫିଚରଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହା ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ସିଷ୍ଟମ୍‍ ଜରିଆରେ କଲ୍‌ର ମାର୍ଗ ବଦଳାଇପାରେ"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"କଲ୍‍ କରିବାର ଅନୁଭୂତି ବଢ଼ାଇବାକୁ ସିଷ୍ଟମ୍‍ ଜରିଆରେ ଆପର କଲ୍‍ଗୁଡ଼ିକୁ ରୁଟ୍‍ କରିବାକୁ ଏହାକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ସିଷ୍ଟମ୍‍ ମାଧ୍ୟମରେ କଲ୍‍ଗୁଡ଼ିକୁ ଦେଖିଥାଏ ଏବଂ ନିୟନ୍ତ୍ରଣ କରିଥାଏ।"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ଆପଣଙ୍କ ଫେସ୍ ମଡେଲକୁ ଡିଲିଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ, ତା\'ପରେ ପୁଣି ଆପଣଙ୍କ ଫେସ୍ ଯୋଗ କରନ୍ତୁ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ଫେସ୍ ଅନଲକ୍ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ଫୋନକୁ ଦେଖି ଏହାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ଫେସ ଅନଲକ ବ୍ୟବହାର କରିବା ପାଇଁ, ସେଟିଂସ ଏବଂ ଗୋପନୀୟତାରେ "<b>"କ୍ୟାମେରା ଆକ୍ସେସ"</b>"କୁ ଚାଲୁ କରନ୍ତୁ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ଅନଲକ୍ କରିବା ପାଇଁ ଆହୁରି ଅଧିକ ଉପାୟ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ଏକ ଟିପଚିହ୍ନ ଯୋଗ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକ୍"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> ପ୍ରସ୍ତୁତ କରାଯାଉଛି।"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ଆପ୍‍ ଆରମ୍ଭ କରାଯାଉଛି।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ବୁଟ୍‍ ସମାପ୍ତ କରୁଛି।"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ସ୍କ୍ରିନ୍ ବନ୍ଦ କରିବେ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ଆପଣଙ୍କ ଟିପଚିହ୍ନ ସେଟ୍ ଅପ୍ କରିବା ସମୟରେ, ଆପଣ ପାୱାର ବଟନ୍ ଦବାଇଛନ୍ତି।\n\nଏହା ସାଧାରଣତଃ ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ବନ୍ଦ କରେ।"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ବନ୍ଦ କରନ୍ତୁ"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ବାତିଲ୍ କରନ୍ତୁ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ସେଟଅପ କରିବା ଜାରି ରଖିବେ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ଆପଣ ପାୱାର ବଟନ ଦବାଇଛନ୍ତି — ଏହା ସାଧାରଣତଃ ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ବନ୍ଦ କରିଥାଏ।\n\nଆପଣଙ୍କ ଟିପଚିହ୍ନ ସେଟ ଅପ କରିବା ସମୟରେ ଧୀରେ ଟାପ କରିବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ସ୍କ୍ରିନ ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ସେଟଅପ କରିବା ଜାରି ରଖ"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ଆପଣଙ୍କ ଟିପଚିହ୍ନ ଯାଞ୍ଚ କରିବା ଜାରି ରଖିବେ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ଆପଣ ପାୱାର ବଟନ ଦବାଇଛନ୍ତି — ଏହା ସାଧାରଣତଃ ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ବନ୍ଦ କରିଥାଏ।\n\nଆପଣଙ୍କ ଟିପଚିହ୍ନ ଯାଞ୍ଚ କରିବା ପାଇଁ ଧୀରେ ଟାପ କରିବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ସ୍କ୍ରିନ ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ଜାରି ରଖନ୍ତୁ"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ଚାଲୁଛି"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ଗେମ୍‌କୁ ଫେରିଆସିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ଗେମ୍ ଚୟନ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 1877ebecf597..6826a2d73cd7 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਕਾਲਾਂ ਕਰਨ ਲਈ IMS ਸੇਵਾ ਵਰਤਣ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ਫ਼ੋਨ ਸਥਿਤੀ ਅਤੇ ਪਛਾਣ ਪੜ੍ਹੋ"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ਐਪ ਨੂੰ ਡੀਵਾਈਸ ਦੀਆਂ ਫ਼ੋਨ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਇਜਾਜ਼ਤ ਐਪ ਨੂੰ ਫ਼ੋਨ ਨੰਬਰ ਅਤੇ ਡੀਵਾਈਸ ਆਈ.ਡੀ. ਨਿਰਧਾਰਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ, ਇੱਕ ਕਾਲ ਕਿਰਿਆਸ਼ੀਲ ਹੈ ਜਾਂ ਨਹੀਂ ਅਤੇ ਰਿਮੋਟ ਨੰਬਰ ਇੱਕ ਕਾਲ ਨਾਲ ਕਨੈਕਟ ਹੈ ਜਾਂ ਨਹੀਂ।"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ਮੂਲ ਟੈਲੀਫ਼ੋਨੀ ਸਥਿਤੀ ਅਤੇ ਪਛਾਣ ਨੂੰ ਪੜ੍ਹੋ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ਐਪ ਨੂੰ ਡੀਵਾਈਸ \'ਤੇ ਮੂਲ ਟੈਲੀਫ਼ੋਨੀ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ਸਿਸਟਮ ਰਾਹੀਂ ਕਾਲਾਂ ਰੂਟ ਕਰੋ"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"ਕਾਲ ਕਰਨ ਦੇ ਅਨੁਭਵ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਲਈ ਐਪ ਨੂੰ ਇਸਦੀਆਂ ਕਾਲਾਂ ਨੂੰ ਸਿਸਟਮ ਰਾਹੀਂ ਰੂਟ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ਸਿਸਟਮ ਰਾਹੀਂ ਕਾਲਾਂ ਦੇਖੋ ਅਤੇ ਕੰਟਰੋਲ ਕਰੋ।"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ਆਪਣੇ ਚਿਹਰੇ ਦਾ ਮਾਡਲ ਮਿਟਾਉਣ ਲਈ ਟੈਪ ਕਰੋ, ਫਿਰ ਆਪਣਾ ਚਿਹਰਾ ਦੁਬਾਰਾ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ਫ਼ੇਸ ਅਣਲਾਕ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ਆਪਣੇ ਫ਼ੋਨ ਵੱਲ ਦੇਖ ਕੇ ਇਸਨੂੰ ਅਣਲਾਕ ਕਰੋ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ਫ਼ੇਸ ਅਣਲਾਕ ਨੂੰ ਵਰਤਣ ਲਈ, ਸੈਟਿੰਗਾਂ &gt; ਪਰਦੇਦਾਰੀ ਵਿੱਚ ਜਾ ਕੇ "<b>"ਕੈਮਰਾ ਪਹੁੰਚ"</b>" ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ਅਣਲਾਕ ਕਰਨ ਦੇ ਹੋਰ ਤਰੀਕਿਆਂ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਅਣਲਾਕ"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> ਤਿਆਰ ਕਰ ਰਿਹਾ ਹੈ।"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ਐਪਸ ਚਾਲੂ ਕਰ ਰਿਹਾ ਹੈ।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ਬੂਟ ਪੂਰਾ ਕਰ ਰਿਹਾ ਹੈ।"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ਕੀ ਸਕ੍ਰੀਨ ਬੰਦ ਕਰਨੀ ਹੈ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨ ਵੇਲੇ, ਤੁਸੀਂ ਪਾਵਰ ਬਟਨ ਨੂੰ ਦਬਾਇਆ ਹੈ।\n\nਇਹ ਆਮ ਤੌਰ \'ਤੇ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਨੂੰ ਬੰਦ ਕਰ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ਬੰਦ ਕਰੋ"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ਰੱਦ ਕਰੋ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ਕੀ ਸੈੱਟਅੱਪ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ਤੁਸੀਂ ਪਾਵਰ ਬਟਨ ਨੂੰ ਦਬਾਇਆ ਹੈ — ਇਹ ਆਮ ਤੌਰ \'ਤੇ ਸਕ੍ਰੀਨ ਨੂੰ ਬੰਦ ਕਰ ਦਿੰਦਾ ਹੈ।\n\nਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਕਰਦੇ ਸਮੇਂ ਹਲਕਾ ਜਿਹਾ ਟੈਪ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ਸਕ੍ਰੀਨ ਬੰਦ ਕਰੋ"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ਸੈੱਟਅੱਪ ਜਾਰੀ ਰੱਖੋ"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ਕੀ ਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ਤੁਸੀਂ ਪਾਵਰ ਬਟਨ ਨੂੰ ਦਬਾਇਆ ਹੈ — ਇਹ ਆਮ ਤੌਰ \'ਤੇ ਸਕ੍ਰੀਨ ਨੂੰ ਬੰਦ ਕਰ ਦਿੰਦਾ ਹੈ।\n\nਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਹਲਕਾ ਜਿਹਾ ਟੈਪ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ਸਕ੍ਰੀਨ ਬੰਦ ਕਰੋ"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ਜਾਰੀ ਰੱਖੋ"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ਚੱਲ ਰਿਹਾ ਹੈ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ਗੇਮ \'ਤੇ ਵਾਪਸ ਜਾਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ਗੇਮ ਚੁਣੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 6f2198ecd74c..e16d2afcb190 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Zezwala aplikacji na korzystanie z usługi komunikatora, by nawiązywać połączenia bez Twojego udziału."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"odczytywanie stanu i informacji o telefonie"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Pozwala aplikacji na dostęp do funkcji telefonicznych urządzenia. Aplikacja z tym uprawnieniem może odczytać numer telefonu i identyfikator urządzenia, sprawdzić, czy połączenie jest aktywne, oraz poznać numer, z którym jest nawiązane połączenie."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"odczyt podstawowych informacji na temat stanu usług telefonicznych i tożsamości"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Zezwól na dostęp aplikacji do podstawowych funkcji telefonicznych na urządzeniu."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"przekazywanie połączeń przez system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Zezwala aplikacji na przekazywanie połączeń przez system, by poprawić ich jakość."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"przeglądanie i kontrolowanie połączeń w systemie."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Kliknij, aby usunąć model twarzy, a następnie ponownie dodaj skan twarzy"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Skonfiguruj rozpoznawanie twarzy"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Popatrz na ekran telefonu, aby go odblokować"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Aby używać rozpoznawania twarzy, włącz "<b>"dostęp do aparatu"</b>" w Ustawieniach i prywatności"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Skonfiguruj więcej sposobów odblokowywania"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Kliknij, aby dodać odcisk palca"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odblokowywanie odciskiem palca"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Przygotowuję aplikację <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Uruchamianie aplikacji."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Kończenie uruchamiania."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Wyłączyć ekran?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Podczas konfigurowania odcisku palca naciśnięto przycisk zasilania.\n\nZwykle powoduje to wyłączenie ekranu."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Wyłącz"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Anuluj"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Kontynuować konfigurowanie?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Naciśnięto przycisk zasilania — zwykle powoduje to wyłączenie ekranu.\n\nKlikaj delikatnie podczas konfigurowania odcisku palca."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Wyłącz ekran"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Konfiguruj dalej"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Kontynuować weryfikację odcisku palca?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Naciśnięto przycisk zasilania — zwykle powoduje to wyłączenie ekranu.\n\nKliknij delikatnie, aby zweryfikować odcisk palca."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Wyłącz ekran"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Dalej"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Działa <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Kliknij, by wrócić do gry"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Wybierz grę"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 8e7cc3dd6a9a..52d7992d456c 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite que o app use o serviço de mensagens instantâneas para fazer chamadas sem sua intervenção."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ler status e identidade do telefone"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite que o app acesse os recursos de telefonia do dispositivo. Esta permissão autoriza o app a determinar o número de telefone e IDs de dispositivo, quando uma chamada está ativa, e o número remoto conectado a uma chamada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ler status e identidade básicos de telefonia"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite que o app acesse os recursos básicos de telefonia do dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"encaminhar chamadas pelo sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite que o app encaminhe suas chamadas por meio do sistema para melhorar a experiência com chamadas."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ver e controlar chamadas pelo sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o Desbloqueio facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o smartphone olhando para ele"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar o Desbloqueio facial, ative o "<b>"acesso à câmera"</b>" em Configurações &gt; Privacidade"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear a tela"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toque para adicionar uma impressão digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueio por impressão digital"</string>
@@ -1208,7 +1211,7 @@
<string name="whichOpenLinksWith" msgid="1120936181362907258">"Abrir links com"</string>
<string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Abrir links com <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
<string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Abrir links do domínio <xliff:g id="HOST">%1$s</xliff:g> usando <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
- <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Conceder acesso"</string>
+ <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Permitir acesso"</string>
<string name="whichEditApplication" msgid="6191568491456092812">"Editar com"</string>
<string name="whichEditApplicationNamed" msgid="8096494987978521514">"Editar com %1$s"</string>
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"Editar"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Concluindo a inicialização."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Desligar a tela?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Durante a configuração da sua impressão digital, você pressionou o botão liga/desliga.\n\nNormalmente, essa ação desliga a tela."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Desligar"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancelar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continuar a configuração?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Você pressionou o botão liga/desliga. Normalmente, essa ação desliga a tela.\n\nToque levemente na tela durante a configuração da impressão digital."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Desligar a tela"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continuar configuração"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a verificação da digital?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Você pressionou o botão liga/desliga. Normalmente, essa ação desliga a tela.\n\nToque levemente na tela para verificar sua impressão digital."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar a tela"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuar"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> em execução"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Toque para voltar ao jogo"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Escolha o jogo"</string>
@@ -2110,12 +2117,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Esta notificação foi rebaixada a Silenciosa. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificação foi classificada com maior prioridade. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificação foi classificada com menor prioridade. Toque para enviar seu feedback."</string>
- <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificações aprimoradas"</string>
- <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"As ações e respostas sugeridas agora são fornecidas pelas notificações aprimoradas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
+ <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificações avançadas"</string>
+ <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"As ações e respostas sugeridas agora são fornecidas pelas notificações avançadas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Desativar"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saiba mais"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações aprimoradas substituíram as notificações adaptáveis. Esse recurso exibe ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações aprimoradas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações avançadas substituíram as notificações adaptáveis. Esse recurso exibe ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações avançadas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informação do modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A bateria pode acabar antes da recarga normal"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"A Economia de bateria foi ativada para aumentar a duração da carga"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index bf8ff8e81a8c..2ea9f39046d1 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite que a app utilize o serviço IMS para fazer chamadas sem a sua intervenção."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ler o estado e a identidade do telemóvel"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite que a app aceda às funcionalidades de telefone do dispositivo. Esta autorização permite que a app determine o número de telefone e IDs do dispositivo, se alguma chamada está ativa e qual o número remoto ligado por uma chamada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ler o estado e a identidade de telefonia básicos"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite à app aceder às funcionalidades básicas de telefonia do dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"encaminhar chamadas através do sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite que a app encaminhe as respetivas chamadas através do sistema de modo a melhorar a experiência da chamada."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ver e controlar chamadas através do sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para eliminar o seu modelo de rosto e, em seguida, adicione o seu rosto novamente"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configure o Desbloqueio facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o telemóvel ao olhar para ele"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para utilizar o Desbloqueio facial, ative o "<b>"Acesso à câmara"</b>" em Definições &gt; Privacidade"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toque para adicionar uma impressão digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueio por impressão digital"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"A preparar o <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"A iniciar aplicações"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"A concluir o arranque."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Pretende desligar o ecrã?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Quando configurou a sua impressão digital, premiu o botão ligar/desligar.\n\nGeralmente, esta ação desativa o seu ecrã."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Desligar"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancelar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continuar a configuração?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente ao configurar a sua impressão digital."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Desligar ecrã"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continuar configur."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a validar a impressão digital?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente para validar a sua impressão digital."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar ecrã"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuar"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> em execução"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Toque para regressar ao jogo."</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Selecionar jogo"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 8e7cc3dd6a9a..52d7992d456c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite que o app use o serviço de mensagens instantâneas para fazer chamadas sem sua intervenção."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ler status e identidade do telefone"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite que o app acesse os recursos de telefonia do dispositivo. Esta permissão autoriza o app a determinar o número de telefone e IDs de dispositivo, quando uma chamada está ativa, e o número remoto conectado a uma chamada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ler status e identidade básicos de telefonia"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite que o app acesse os recursos básicos de telefonia do dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"encaminhar chamadas pelo sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite que o app encaminhe suas chamadas por meio do sistema para melhorar a experiência com chamadas."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ver e controlar chamadas pelo sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o Desbloqueio facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o smartphone olhando para ele"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar o Desbloqueio facial, ative o "<b>"acesso à câmera"</b>" em Configurações &gt; Privacidade"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear a tela"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toque para adicionar uma impressão digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueio por impressão digital"</string>
@@ -1208,7 +1211,7 @@
<string name="whichOpenLinksWith" msgid="1120936181362907258">"Abrir links com"</string>
<string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Abrir links com <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
<string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Abrir links do domínio <xliff:g id="HOST">%1$s</xliff:g> usando <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
- <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Conceder acesso"</string>
+ <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Permitir acesso"</string>
<string name="whichEditApplication" msgid="6191568491456092812">"Editar com"</string>
<string name="whichEditApplicationNamed" msgid="8096494987978521514">"Editar com %1$s"</string>
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"Editar"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Concluindo a inicialização."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Desligar a tela?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Durante a configuração da sua impressão digital, você pressionou o botão liga/desliga.\n\nNormalmente, essa ação desliga a tela."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Desligar"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancelar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continuar a configuração?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Você pressionou o botão liga/desliga. Normalmente, essa ação desliga a tela.\n\nToque levemente na tela durante a configuração da impressão digital."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Desligar a tela"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continuar configuração"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a verificação da digital?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Você pressionou o botão liga/desliga. Normalmente, essa ação desliga a tela.\n\nToque levemente na tela para verificar sua impressão digital."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar a tela"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuar"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> em execução"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Toque para voltar ao jogo"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Escolha o jogo"</string>
@@ -2110,12 +2117,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Esta notificação foi rebaixada a Silenciosa. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificação foi classificada com maior prioridade. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificação foi classificada com menor prioridade. Toque para enviar seu feedback."</string>
- <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificações aprimoradas"</string>
- <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"As ações e respostas sugeridas agora são fornecidas pelas notificações aprimoradas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
+ <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificações avançadas"</string>
+ <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"As ações e respostas sugeridas agora são fornecidas pelas notificações avançadas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Desativar"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saiba mais"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações aprimoradas substituíram as notificações adaptáveis. Esse recurso exibe ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações aprimoradas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações avançadas substituíram as notificações adaptáveis. Esse recurso exibe ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações avançadas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informação do modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A bateria pode acabar antes da recarga normal"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"A Economia de bateria foi ativada para aumentar a duração da carga"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 8f05f9f7c68d..ac5eca7f80b1 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -474,6 +474,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite aplicației să folosească serviciul IMS pentru apeluri, fără intervenția dvs."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"citește starea și identitatea telefonului"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite aplicației să acceseze funcțiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanță conectat printr-un apel."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"să citească informații de bază, precum activitatea și starea telefonului"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite ca aplicația să acceseze funcțiile de telefonie de bază ale dispozitivului."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"să direcționeze apelurile prin intermediul sistemului"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permiteți aplicației să direcționeze apelurile prin intermediul sistemului pentru a îmbunătăți calitatea apelurilor."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Vedeți și controlați apelurile prin intermediul sistemului."</string>
@@ -625,6 +627,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Atingeți pentru a șterge modelul facial, apoi adăugați din nou fața"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurați Deblocarea facială"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Deblocați-vă telefonul uitându-vă la acesta"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pentru a folosi Deblocarea facială, activați "<b>"Accesul la cameră"</b>" în Setări și confidențialitate"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurați mai multe moduri de deblocare"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Atingeți ca să adăugați o amprentă"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Deblocare cu amprenta"</string>
@@ -1296,10 +1299,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Se pregătește <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Se pornesc aplicațiile."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Se finalizează pornirea."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Dezactivați ecranul?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Ați apăsat butonul de pornire în timpul configurării amprentei.\n\nDe obicei, această acțiune dezactivează ecranul."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Dezactivați"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Anulați"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continuați configurarea?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Ați apăsat butonul de pornire. De obicei, această acțiune dezactivează ecranul.\n\nAtingeți ușor când vă configurați amprenta."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Dezactivați ecranul"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continuați configurarea"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuați cu verificarea amprentei?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Ați apăsat butonul de pornire. De obicei, această acțiune dezactivează ecranul.\n\nAtingeți ușor pentru verificarea amprentei."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Dezactivați ecranul"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuați"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Rulează <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Atingeți pentru a reveni la joc"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Alegeți jocul"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 927ec93b7683..618283273b61 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Позволяет приложению совершать звонки с помощью службы IMS без вашего вмешательства."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"Получение данных о статусе телефона"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Приложение получит доступ к функциям телефона на устройстве. Кроме того, оно сможет определять номера телефонов и серийные номера моделей, состояние активности вызова, а также удаленные номера, с которыми установлено соединение."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"Считывание статуса и идентификационной информации телефонии"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Приложение получит доступ к основным функциям телефонии на устройстве."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"перенаправлять звонки в системе"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Приложение сможет перенаправлять звонки в системе с целью улучшения качества связи."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Управление вызовами через систему"</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Нажмите, чтобы удалить модель лица, а затем добавьте ее снова."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Настройка фейсконтроля"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Вы сможете разблокировать телефон, просто посмотрев на него."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Чтобы использовать фейсконтроль, разрешите "<b>"доступ к камере"</b>". Для этого перейдите в настройки и нажмите \"Конфиденциальность\"."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Настройте другие способы разблокировки"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Нажмите, чтобы добавить отпечаток пальца."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Разблокировка по отпечатку пальца"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Подготовка приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\"..."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск приложений."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Окончание загрузки..."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Отключить экран?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Во время настройки отпечатка пальца вы нажали кнопку питания.\n\nОбычно это действие приводит к отключению экрана."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Отключить"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Отмена"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Продолжить настройку?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Вы нажали кнопку питания. Обычно это приводит к отключению экрана.\n\nПри добавлении отпечатка пальца слегка прикоснитесь к кнопке."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Отключить экран"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Продолжить настройку"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Продолжить сканирование отпечатка?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Вы нажали кнопку питания. Обычно это приводит к отключению экрана.\n\nЧтобы отсканировать отпечаток пальца, слегка прикоснитесь к кнопке."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Отключить экран"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Продолжить"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Приложение <xliff:g id="APP">%1$s</xliff:g> запущено"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Нажмите, чтобы вернуться в игру."</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Выберите игру"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index c63708fbc459..c5e76c8afb7f 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"ඔබේ මැදිහත්වීමකින් තොරව ඇමතුම් සිදු කිරීමට IMS සේවාව භාවිතයට යෙදුමට ඉඩ දෙන්න."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"දුරකථනයේ තත්වය සහ අනන්‍යතාවය කියවීම"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"උපාංගයේ දුරකථන විශේෂාංග වෙත ප්‍රවේශයට යෙදුමට ඉඩ දෙයි. ඇමතුම සක්‍රිය වුවත්, සහ ඇමතුමකින් දුරස්ථ අංකය සම්බන්ධ වුවත් දුරකථන අංකය සහ උපාංග ID හඳුනා ගැනීමට මෙම අවසරය යෙදුමට ඉඩ දෙයි."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"මූලික දුරකථන තත්ත්වය සහ අනන්‍යතාව කියවන්න"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"උපාංගයේ මූලික දුරකථන විශේෂාංග වෙත ප්‍රවේශ වීමට යෙදුමට ඉඩ දෙයි."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"පද්ධතිය හරහා ඇමතුම් මාර්ගගත කරන්න"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"ඇමතුම් අත්දැකීම වැඩිදියුණු කිරීම සඳහා යෙදුමට පද්ධතිය හරහා එහි ඇමතුම් මාර්ගගත කිරීමට ඉඩ දෙයි."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"පද්ධතිය හරහා ඇමතුම් බලන්න සහ පාලනය කරන්න."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ඔබගේ මුහුණත ආකෘතිය මැකීමට තට්ටු කරන්න, අනතුරුව ඔබගේ මුහුණ නැවත එක් කරන්න"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"මුහුණෙන් අගුළු හැරීම පිහිටුවන්න"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ඔබගේ දුරකථනය දෙස බැලීමෙන් එහි අගුලු හරින්න"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"මුහුණෙන් අගුලු හැරීම භාවිත කිරීමට, සැකසීම් &gt; පෞද්ගලිකත්වය තුළ "<b>"කැමරා ප්‍රවේශය"</b>" ක්‍රියාත්මක කරන්න"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"අගුලු හැරීමට තවත් ක්‍රම සකසන්න"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ඇඟිලි සලකුණක් එක් කිරීමට තට්ටු කරන්න"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ඇඟිලි සලකුණු අගුළු හැරීම"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> සූදානම් කරමින්."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"යෙදුම් ආරම්භ කරමින්."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ඇරඹුම අවසාන කරමින්."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"තිරය ක්‍රියාවිරහිත කරන්නද?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ඔබගේ ඇඟිලි සලකුණ පිහිටුවන අතරතුර ඔබ බල බොත්තම එබුවේය.\n\nමෙය සාමාන්‍යයෙන් ඔබගේ තිරය ක්‍රියාවිරහිත කරයි."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ක්‍රියාවිරහිත කරන්න"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"අවලංගු කරන්න"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"පිහිටුවීම දිගටම කරන්නද?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ඔබ බල බොත්තම එබුවේය — සාමාන්‍යයෙන් මෙය තිරය ක්‍රියාවිරහිත කරයි.\n\nඔබගේ ඇඟිලි සලකුණ පිහිටුවන අතරතුර සැහැල්ලුවෙන් තට්ටු කිරීමට උත්සාහ කරන්න."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"තිරය අක්‍රිය කරන්න"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"පිහිටුවීම දිගටම කර."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ඔබගේ ඇඟිලි සලකුණ සත්‍යාපනය දිගටම කරන්නද?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ඔබ බල බොත්තම එබුවේය — සාමාන්‍යයෙන් මෙය තිරය ක්‍රියාවිරහිත කරයි.\n\nඔබගේ ඇඟිලි සලකුණ සත්‍යාපනය කිරීමට සැහැල්ලුවෙන් තට්ටු කිරීමට උත්සාහ කරන්න."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"තිරය අක්‍රිය කරන්න"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ඉදිරියට යන්න"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ධාවනය වෙමින්"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ක්‍රීඩාව වෙත ආපසු යාමට තට්ටු කරන්න"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ක්‍රීඩාව තෝරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 66451763c07f..dfd34e7ce7b2 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Umožňuje aplikácii používať službu okamžitých správ (IMS) na volanie bez intervencie používateľa."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"čítať stav a identitu telefónu"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Umožňuje aplikácii pristupovať k telefónnym funkciám zariadenia. Aplikácia s týmto povolením môže určiť telefónne číslo a ID zariadenia, či práve prebieha hovor, a vzdialené číslo, s ktorým je prostredníctvom hovoru nadviazané spojenie."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"čítanie základného stavu a identity súvisiacich s telefonovaním"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Umožňuje aplikácii prístup k základným telefonickým funkciám zariadenia."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"presmerovanie hovorov cez systém"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Umožňuje aplikácii presmerovať hovory cez systém na účely zlepšenia kvality hovorov."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"zobrazenie a kontrola hovorov prostredníctvom systému."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Klepnutím odstráňte model tváre a potom znova pridajte svoju tvár"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavte odomknutie tvárou"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Odomykajte telefón tak, že sa naň pozriete"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Ak chcete používať odomknutie tvárou, v sekcii Nastavenia &gt; Ochrana súkromia zapnite "<b>"prístup ku kamere"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavte viac spôsobov odomknutia"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Klepnutím pridajte odtlačok prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odomknutie odtlačkom prsta"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Pripravuje sa aplikácia <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Prebieha spúšťanie aplikácií."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Prebieha dokončovanie spúšťania."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Chcete vypnúť obrazovku?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Pri nastavovaní odtlačku prsta ste stlačili vypínač.\n\nObrazovka sa tým zvyčajne vypne."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Vypnúť"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Zrušiť"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Chcete pokračovať v nastavovaní?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Stlačili ste vypínač. Obvykle tým vypnete obrazovku.\n\nPri nastavovaní odtlačku prsta skúste klepnúť jemne."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Vypnúť obrazovku"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Pokračovať v nastav."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Pokračovať v overovaní odtlačku prsta?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Stlačili ste vypínač. Obvykle tým vypnete obrazovku.\n\nAk chcete overiť odtlačok prsta, skúste klepnúť jemne."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Vypnúť obrazovku"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Pokračovať"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Spustená aplikácia: <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Klepnutím prejdete späť do hry"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Vyberte hru"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 935b1e88752d..931fc5b3bcfb 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Aplikaciji dovoljuje uporabo storitev IMS za opravljanje klicev brez vašega posredovanja."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"branje stanja in identitete telefona"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Aplikaciji omogoča dostop do funkcij telefona v napravi. S tem dovoljenjem lahko aplikacija določi telefonsko številko in ID-je naprave, določi lahko tudi, ali je klic aktiven, in oddaljeno številko, s katero je klic povezan."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"branje stanja osnovne telefonije in identitete"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Aplikaciji omogoča dostop do osnovnih funkcij telefonije v napravi."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"Usmeri klice prek sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Dovoli, da aplikacija usmeri klice prek sistema, da se tako izboljša izkušnja klicanja."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ogled in nadzor klicev prek sistema."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dotaknite se, da izbrišete model obraza, in nato znova dodajte obraz."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavitev odklepanja z obrazom"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Odklenite telefon tako, da ga pogledate."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Če želite uporabljati odklepanje z obrazom, v meniju »Nastavitve« &gt; »Zasebnost« vklopite možnost "<b>"Dostop do fotoaparata"</b>"."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavite več načinov odklepanja"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dotaknite se, da dodate prstni odtis."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odklepanje s prstnim odtisom"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Pripravljanje aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Zagon aplikacij."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Dokončevanje zagona."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Želite izklopiti zaslon?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Pri nastavljanju prstnega odtisa ste pritisnili gumb za vklop.\n\nS tem običajno izklopite zaslon."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Izklopi"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Prekliči"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Ali želite nadaljevati nastavitev?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnili ste gumb za vklop, s čimer običajno izklopite zaslon.\n\nPoskusite se narahlo dotakniti med nastavljanjem prstnega odtisa."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Izklopi zaslon"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Nadaljuj nastavitev"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Želite nadaljevati preverjanje prstnega odtisa?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnili ste gumb za vklop, s čimer običajno izklopite zaslon.\n\nZa preverjanje prstnega odtisa se poskusite narahlo dotakniti."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Izklopi zaslon"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Nadaljuj"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> se izvaja"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Dotaknite se za vrnitev v igro"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Izberite igro"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 7bd3a53cdd68..937332b12027 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Lejon aplikacionin të përdorë shërbimin IMS për të kryer telefonata pa ndërhyrjen tënde."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lexo statusin e telefonit dhe identitetin"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Lejon aplikacionin të hyjë në funksionet telefonike të pajisjes. Kjo leje i mundëson aplikacionit të përcaktojë numrin e telefonit dhe ID-të e pajisjes, nëse një telefonatë është aktive apo nëse numri në distancë është i lidhur me një telefonatë."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lexo statusin dhe identitetin telefonik bazë"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Lejo që aplikacioni të ketë qasje në veçoritë telefonike bazë në pajisje."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"kalon telefonatat përmes sistemit"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Lejon që aplikacioni të kalojë telefonatat përmes sistemit për të përmirësuar përvojën e telefonatës."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Shiko dhe kontrollo telefonatat nëpërmjet sistemit."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Trokit për të fshirë modelin tënd të fytyrës, pastaj shtoje përsëri fytyrën tënde"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfiguro \"Shkyçjen me fytyrë\""</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Shkyçe telefonin duke parë tek ai"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Për të përdorur \"Shkyçjen me fytyrë\", aktivizo "<b>"Qasjen te kamera"</b>" te \"Cilësimet\" &gt; \"Privatësia\""</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfiguri më shumë mënyra për të shkyçur"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Trokit për të shtuar një gjurmë gishti"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Shkyçja me gjurmën e gishtit"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Po përgatit <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Aplikacionet e fillimit."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Po përfundon nisjen."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Të fiket ekrani?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Gjatë konfigurimit të gjurmës së gishtit tënd, ke shtypur butonin e \"Energjisë\".\n\nKjo zakonisht fik ekranin tënd."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Fik"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Anulo"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Të vazhdohet konfigurimi?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Shtype butonin e energjisë — zakonisht, kjo e fik ekranin.\n\nProvo të trokasësh lehtë ndërkohë që konfiguron gjurmën e gishtit."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Fik ekranin"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Vazhdo konfigurimin"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Të vazhdohet verifikimi i gjurmës?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Shtype butonin e energjisë — zakonisht, kjo e fik ekranin.\n\nProvo të trokasësh lehtë për të verifikuar gjurmën e gishtit."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Fik ekranin"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Vazhdo"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> është në punë"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Trokit për t\'u kthyer te loja"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Zgjidh një lojë"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 97b233c0af75..72fd908fc7ae 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -474,6 +474,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Дозвољава апликацији да користи услугу размене тренутних порука да би упућивала позиве без ваше интервенције."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"читање статуса и идентитета телефона"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Дозвољава апликацији да приступа функцијама телефона на уређају. Ова дозвола омогућава апликацији да утврди број телефона и ИД-ове уређаја, затим да ли је позив активан, као и број даљинског уређаја са којим је успостављен позив."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"очитавање основног телефонског статуса и идентитета"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Омогућава апликацији да приступа основним телефонским функцијама уређаја."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"преусмеравање позива преко система"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Дозвољава апликацији да преусмерава позиве преко система да би побољшала доживљај позивања."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"преглед и контрола позива преко система."</string>
@@ -625,6 +627,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Додирните да бисте избрисали модел лица, па поново додајте своје лице"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Подесите откључавање лицем"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Откључајте телефон тако што ћете га погледати"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Да бисте користили откључавање лицем, укључите "<b>"приступ камери"</b>" у одељку Подешавања &gt; Приватност"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Подесите још начина за откључавање"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Додирните да бисте додали отисак прста"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Откључавање отиском прста"</string>
@@ -1296,10 +1299,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Припрема се <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Покретање апликација."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Завршавање покретања."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Желите да искључите екран?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Притисли сте дугме за укључивање током подешавања отиска прста.\n\nТако се најчешће искључује екран."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Искључи"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Откажи"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Желите ли да наставите са подешавањем?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Притиснули сте дугме за укључивање – тиме обично искључујете екран.\n\nПробајте лагано да додирнете док подешавате отисак прста."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Искључи екран"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Настави подешавање"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Настављате верификацију отиска прста?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Притиснули сте дугме за укључивање – тиме обично искључујете екран.\n\nПробајте лагано да додирнете да бисте верификовали отисак прста."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Искључи екран"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Настави"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Апликација <xliff:g id="APP">%1$s</xliff:g> је покренута"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Додирните да бисте се вратили у игру"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Одаберите игру"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 740cfb977c5d..e078da0510da 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Tillåter att appen använder tjänsten för snabbmeddelanden för att ringa samtal utan åtgärd från dig."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"läsa telefonens status och identitet"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Tillåter att appen kommer åt enhetens telefonfunktioner. Med den här behörigheten tillåts appen att identifiera mobilens telefonnummer och enhets-ID, om ett samtal pågår och vilket nummer samtalet är kopplat till."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"läsa grundläggande uppgifter om telefonistatus och identitet"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Ger appen åtkomst till grundläggande telefonifunktioner på enheten."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"dirigera samtal via systemet"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Appen tillåts att dirigera samtal via systemet för att förbättra samtalsupplevelsen."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"visa och styra samtal via systemet."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tryck för att radera ansiktsmodellen och lägg sedan till ansiktet igen"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurera ansiktslås"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Lås upp telefonen genom att titta på den"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Om du vill använda ansiktslås aktiverar du "<b>"Kameraåtkomst"</b>" i Inställningar &gt; Integritet"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurera fler sätt att låsa upp"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tryck för att lägga till ett fingeravtryck"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingeravtryckslås"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> förbereds."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Appar startas."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Uppgraderingen är klar."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vill du stänga av skärmen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Du tryckte på strömbrytaren när du skulle konfigurera fingeravtrycket.\n\nDet brukar leda till att skärmen stängs av."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Stäng av"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Avbryt"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Vill du fortsätta med konfigureringen?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du tryckte på strömbrytaren, vilket vanligtvis stänger av skärmen.\n\nTesta att trycka lätt när du konfigurerar fingeravtrycket."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Stäng av skärmen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Fortsätt konfigurera"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vill du verifiera ditt fingeravtryck?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du tryckte på strömbrytaren, vilket vanligtvis stänger av skärmen.\n\nTesta att trycka lätt för att verifiera ditt fingeravtryck."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Stäng av skärmen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Fortsätt"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> körs"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Återgå till spelet genom att trycka här"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Välj spel"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index fd1faa080a47..99fd1079476f 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Huruhusu programu kutumia huduma ya IMS kupiga simu bila udhibiti wako."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"kusoma hali na kitambulisho cha simu"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Huruhusu programu kufikia vipengele vya simu vilivyo kwenye kifaa. Idhini hii inaruhusu programu kutambua nambari ya simu na kifaa, kama kuna simu inayopigwa, na nambari ya mbali iliyounganishwa kwenye simu."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"kusoma utambulisho na hali ya msingi ya simu"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Huruhusu programu kufikia vipengele vya msingi vya simu kwenye kifaa."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"elekeza simu kupitia mfumo"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Huruhusu programu kuelekeza simu zake kupitia mfumo ili kuboresha hali ya kupiga simu."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"kuona na kudhibiti simu kupitia mfumo."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Gusa ili ufute muundo wa uso wako, kisha uweke uso wako tena"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Weka mipangilio ya Kufungua kwa Uso"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Fungua simu yako kwa kuiangalia"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Ili utumie kipengele cha kufungua kwa uso, washa kipengele cha "<b>"ufikiaji wa Kamera"</b>" katika Mipangilio na Faragha"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Weka mipangilio ya mbinu zaidi za kufungua"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Gusa ili uweke alama ya kidole"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Kufungua kwa Alama ya Kidole"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Inaandaa <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Programu zinaanza"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Inamaliza kuwasha."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ungependa kuzima skrini?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Ulipokuwa ukiweka alama ya kidole chako, ulibonyeza Kitufe cha kuwasha/kuzima.\n\nKwa kawaida, hatua hii huzima skrini yako."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Zima"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Ghairi"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Ungependa kuendelea kuweka mipangilio?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Umebonyeza kitufe cha kuwasha/kuzima — kwa kawaida, hali hii huzima skrini.\n\nJaribu kugusa taratibu unapoweka mipangilio ya alama ya kidole chako."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Zima skrini"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Endelea kuweka mipangilio"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Utaendelea kuthibitisha alama ya kidole chako?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Umebonyeza kitufe cha kuwasha/kuzima — kwa kawaida, hali hii huzima skrini.\n\nJaribu kugusa taratibu ili uthibitishe alama ya kidole chako."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Zima skrini"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Endelea"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> inaendelea"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Gusa ili urudi kwenye mchezo"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Chagua mchezo"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 0690c319bbf6..0be25cd879ed 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"உங்கள் குறுக்கீடின்றி IMS சேவையைப் பயன்படுத்தி அழைப்பதற்கு, ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"மொபைல் நிலை மற்றும் அடையாளத்தைப் படித்தல்"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"சாதனத்தின் மொபைல் அம்சங்களை அணுகப் ஆப்ஸை அனுமதிக்கிறது. மொபைல் மற்றும் சாதன ஐடிகள், அழைப்பு செயலில் உள்ளதா மற்றும் அழைப்பு மூலம் இணைக்கப்பட்ட தொலைக் கட்டுப்பாட்டு எண் ஆகியவற்றைத் தீர்மானிக்க இந்த அனுமதி ஆப்ஸை அனுமதிக்கிறது."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"அடிப்படையான டெலிஃபோனி நிலையையும் அடையாளத்தையும் படித்தல்"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"சாதனத்திலுள்ள அடிப்படையான டெலிஃபோனி அம்சங்களை அணுக ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"சிஸ்டம் மூலம் அழைப்புகளை ரூட் செய்தல்"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"அழைக்கும் அனுபவத்தை மேம்படுத்தும் பொருட்டு, சிஸ்டம் மூலம் தனது அழைப்புகளை ரூட் செய்ய ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"சிஸ்டம் மூலமாக அழைப்புகளைப் பார்த்தலும் கட்டுப்படுத்துதலும்."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"முகத் தோற்றப் பதிவைத் தட்டி நீக்கிவிட்டு உங்கள் முகத்தை மீண்டும் சேர்க்கவும்"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"\'முகம் காட்டித் திறத்தல்\' அம்சத்தை அமைத்தல்"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"மொபைலைப் பார்ப்பதன் மூலம் அதை அன்லாக் செய்யலாம்"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"முகம் காட்டித் திறத்தல் அம்சத்தைப் பயன்படுத்த, அமைப்புகள் &gt; தனியுரிமை என்பதற்குச் சென்று "<b>"கேமரா அணுகலை"</b>" இயக்கவும்"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"அன்லாக் செய்ய மேலும் பல வழிகளை அமையுங்கள்"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"கைரேகையைச் சேர்க்கத் தட்டுங்கள்"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"கைரேகை அன்லாக்"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>ஐத் தயார்செய்கிறது."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ஆப்ஸ் தொடங்கப்படுகின்றன."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"துவக்குதலை முடிக்கிறது."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"திரையை ஆஃப் செய்யவா?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"கைரேகையை அமைக்கும்போது பவர் பட்டனை அழுத்திவிட்டீர்கள்.\n\nஇது திரையை ஆஃப் செய்துவிடும்."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ஆஃப் செய்"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ரத்துசெய்"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"அமைவைத் தொடரவா?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"நீங்கள் பவர் பட்டனை அழுத்தியுள்ளீர்கள் — வழக்கமாக இது திரையை ஆஃப் செய்யும்.\n\nஉங்கள் கைரேகையை அமைக்கும்போது மெதுவாகத் தொடுங்கள்."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"திரையை ஆஃப் செய்"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"அமைவைத் தொடர்க"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"கைரேகைச் சரிபார்ப்பைத் தொடரவா?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"நீங்கள் பவர் பட்டனை அழுத்தியுள்ளீர்கள் — வழக்கமாக இது திரையை ஆஃப் செய்யும்.\n\nஉங்கள் கைரேகையைச் சரிபார்க்க மெதுவாகத் தொடுங்கள்."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"திரையை ஆஃப் செய்"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"தொடர்க"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> இயங்குகிறது"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"கேமிற்குச் செல்ல, தட்டவும்"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"கேமைத் தேர்வுசெய்க"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 313ce593c77a..3aa7b045153c 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -422,7 +422,7 @@
<string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"మీ ఫోన్‌లో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
<string name="permlab_readCallLog" msgid="1739990210293505948">"కాల్ లాగ్‌ను చదవడం"</string>
<string name="permdesc_readCallLog" msgid="8964770895425873433">"ఈ యాప్‌ మీ కాల్ చరిత్రను చదవగలదు."</string>
- <string name="permlab_writeCallLog" msgid="670292975137658895">"కాల్ లాగ్‌ను వ్రాయడం"</string>
+ <string name="permlab_writeCallLog" msgid="670292975137658895">"కాల్ లాగ్‌ను రాయడం"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ టాబ్లెట్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌కు సంబంధించిన డేటాతో సహా మీ Android TV పరికరం కాల్ లాగ్‌ను సవరించడానికి యాప్‌ని అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను తీసివేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ ఫోన్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"మీ ప్రమేయం లేకుండా కాల్స్‌ చేయడం కోసం IMS సేవను ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ఫోన్ స్టేటస్‌ మరియు గుర్తింపుని చదవడం"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"పరికరం యొక్క ఫోన్ ఫీచర్‌లను యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి ఫోన్ నంబర్ మరియు పరికరం IDలను, కాల్ సక్రియంగా ఉందా లేదా అనే విషయాన్ని మరియు కాల్ ద్వారా కనెక్ట్ చేయబడిన రిమోట్ నంబర్‌ను కనుగొనడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ప్రాథమిక టెలిఫోన్ స్టేటస్, గుర్తింపును చదవండి"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"పరికరం తాలూకు ప్రాథమిక టెలిఫోన్ ఫీచర్‌లను యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"కాల్స్‌ను సిస్టమ్ ద్వారా వెళ్లేలా చేయి"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"కాలింగ్ అనుభవాన్ని మెరుగుపరచడం కోసం తన కాల్స్‌ను సిస్టమ్ ద్వారా వెళ్లేలా చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"సిస్టమ్ ద్వారా కాల్స్‌ను చూసి, నియంత్రించండి."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ఫేస్ మోడల్‌ను తొలగించడానికి నొక్కండి, ఆపై మీ ముఖాన్ని మళ్లీ జోడించండి"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ఫేస్ అన్‌లాక్‌ను సెటప్ చేయండి"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"మీ ఫోన్‌ను చూడటం ద్వారా దాన్ని అన్‌లాక్ చేయండి"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ఫేస్ అన్‌లాక్‌ను ఉపయోగించడానికి, సెట్టింగ్‌లు &gt; గోప్యతలో "<b>"కెమెరా యాక్సెస్"</b>"ను ఆన్ చేయండి"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"అన్‌లాక్ చేయడానికి మరిన్ని మార్గాలను సెటప్ చేయండి"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"వేలిముద్రను జోడించడానికి ట్యాప్ చేయండి"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"వేలిముద్ర అన్‌లాక్"</string>
@@ -725,7 +728,7 @@
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"క్యారియర్ సేవలకు అనుబంధించడం"</string>
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"క్యారియర్ సేవలకు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"అంతరాయం కలిగించవద్దును యాక్సెస్ చేయడం"</string>
- <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు వ్రాయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు రాయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"వీక్షణ అనుమతి వినియోగాన్ని ప్రారంభించండి"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"యాప్‌నకు అనుమతి వినియోగాన్ని ప్రారంభించడానికి హోల్డర్‌‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ ఇటువంటి అనుమతి అవసరం ఉండదు."</string>
<string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"యాప్ ఫీచర్‌లను చూడటాన్ని ప్రారంభించండి"</string>
@@ -1020,7 +1023,7 @@
<string name="autofill_emirate" msgid="2544082046790551168">"ఎమిరేట్"</string>
<string name="permlab_readHistoryBookmarks" msgid="9102293913842539697">"మీ వెబ్ బుక్‌మార్క్‌లు మరియు చరిత్రను చదవడం"</string>
<string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"బ్రౌజర్ సందర్శించిన అన్ని URLల చరిత్ర గురించి మరియు అన్ని బ్రౌజర్ బుక్‌మార్క్‌ల గురించి చదవడానికి యాప్‌ను అనుమతిస్తుంది. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
- <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"వెబ్ బుక్‌మార్క్‌లు మరియు చరిత్రను వ్రాయడం"</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"వెబ్ బుక్‌మార్క్‌లు మరియు చరిత్రను రాయడం"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"మీ టాబ్లెట్‌లో నిల్వ చేయబడిన బ్రౌజర్ హిస్టరీని, బుక్‌మార్క్‌లను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా ఎడిట్ చేయడానికి యాప్‌ను అనుమతించవచ్చు. గమనిక: ఈ అనుమతిని థర్డ్ పార్టీ బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌లు అమలు చేయకపోవచ్చు."</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"మీ Android TV పరికరంలో నిల్వ చేసిన బ్రౌజర్ చరిత్ర లేదా బుక్‌మార్క్‌లను సవరించడానికి యాప్‌ని అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను తీసివేయడానికి లేదా సవరించడానికి యాప్‌ని అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ-పక్ష బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు కాకపోవచ్చు."</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"మీ ఫోన్‌లో నిల్వ చేయబడిన బ్రౌజర్ చరిత్రను లేదా బుక్‌మార్క్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా సవరించడానికి యాప్‌ను అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>ని సిద్ధం చేస్తోంది."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"యాప్‌లను ప్రారంభిస్తోంది."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"బూట్‌ను ముగిస్తోంది."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"స్క్రీన్‌ను ఆఫ్ చేయాలా?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"మీ వేలిముద్రను సెటప్ చేస్తున్నప్పుడు, మీరు పవర్ బటన్‌ను నొక్కారు.\n\nఇది సాధారణంగా మీ స్క్రీన్‌ను ఆఫ్ చేస్తుంది."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ఆఫ్ చేయండి"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"రద్దు చేయండి"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"సెటప్‌ను కొనసాగించాలా?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"మీరు పవర్ బటన్‌ను నొక్కారు — ఇది సాధారణంగా స్క్రీన్‌ను ఆఫ్ చేస్తుంది.\n\nమీ వేలిముద్రను సెటప్ చేస్తున్నప్పుడు తేలికగా ట్యాప్ చేయడానికి ట్రై చేయండి."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"స్క్రీన్‌ను ఆఫ్ చేయి"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"సెటప్‌ను కొనసాగించు"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"మీ వేలిముద్ర వెరిఫై‌ను కొనసాగించాలా?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"మీరు పవర్ బటన్‌ను నొక్కారు — ఇది సాధారణంగా స్క్రీన్‌ను ఆఫ్ చేస్తుంది.\n\nమీ వేలిముద్రను వెరిఫై చేయడానికి తేలికగా ట్యాప్ చేయడం ట్రై చేయండి."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"స్క్రీన్‌ను ఆఫ్ చేయి"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"కొనసాగించండి"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> అమలవుతోంది"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"గేమ్‌కి తిరిగి రావడానికి నొక్కండి"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"గేమ్‌ను ఎంచుకోండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index b8d8506febf2..41adb26abfaa 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"อนุญาตให้แอปใช้บริการ IMS เพื่อโทรออกโดยคุณไม่ต้องดำเนินการใดๆ เลย"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"อ่านสถานะและข้อมูลระบุตัวตนของโทรศัพท์"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"อนุญาตให้แอปพลิเคชันเข้าถึงฟีเจอร์โทรศัพท์ของอุปกรณ์ การอนุญาตนี้ทำให้แอปพลิเคชันสามารถตรวจสอบหมายเลขโทรศัพท์และรหัสอุปกรณ์ ตรวจสอบว่ามีการโทรที่ทำงานอยู่หรือไม่ และตรวจสอบหมายเลขระยะไกลที่เชื่อมต่อด้วยการโทร"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"อ่านสถานะการใช้โทรศัพท์เบื้องต้นและข้อมูลระบุตัวตน"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"อนุญาตให้แอปเข้าถึงฟีเจอร์พื้นฐานในการใช้โทรศัพท์ของอุปกรณ์นี้"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"กำหนดเส้นทางการโทรผ่านระบบ"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"อนุญาตให้แอปกำหนดเส้นทางการโทรของแอปผ่านระบบเพื่อปรับปรุงประสบการณ์ในการโทร"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ดูและจัดการการติดต่อผ่านระบบ"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"แตะเพื่อลบรูปแบบใบหน้า แล้วเพิ่มใบหน้าอีกครั้ง"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ตั้งค่าการปลดล็อกด้วยใบหน้า"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ปลดล็อกโทรศัพท์โดยมองไปที่โทรศัพท์"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"หากต้องการใช้ปลดล็อกด้วยใบหน้า ให้เปิด"<b>"การเข้าถึงกล้อง"</b>"ในการตั้งค่าและความเป็นส่วนตัว"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ตั้งค่าการปลดล็อกด้วยวิธีอื่น"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"แตะเพื่อเพิ่มลายนิ้วมือ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ปลดล็อกด้วยลายนิ้วมือ"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"กำลังเตรียม <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"กำลังเริ่มต้นแอปพลิเคชัน"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"เสร็จสิ้นการบูต"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ปิดหน้าจอใช่ไหม"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ขณะตั้งค่าลายนิ้วมือคุณจะต้องกดปุ่มเปิด/ปิด\n\nซึ่งโดยปกติจะเป็นการปิดหน้าจอด้วย"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ปิด"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ยกเลิก"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ตั้งค่าต่อไหม"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"คุณกดปุ่มเปิด/ปิดซึ่งโดยปกติจะเป็นการปิดหน้าจอ\n\nลองแตะเบาๆ ขณะตั้งค่าลายนิ้วมือ"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ปิดหน้าจอ"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ตั้งค่าต่อ"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ยืนยันลายนิ้วมือต่อไหม"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"คุณกดปุ่มเปิด/ปิดซึ่งโดยปกติจะเป็นการปิดหน้าจอ\n\nลองแตะเบาๆ เพื่อยืนยันลายนิ้วมือ"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ปิดหน้าจอ"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ต่อไป"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> กำลังทำงาน"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"แตะเพื่อกลับไปที่เกม"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"เลือกเกม"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 30350a5ab8a7..7f39769d4e77 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Pinapahintulutan ang app na gamitin ang serbisyo ng IMS upang tumawag nang walang pahintulot mo."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"basahin ang katayuan at pagkakakilanlan ng telepono"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Pinapayagan ang app na i-access ang mga tampok ng telepono ng device. Pinapayagan ng pahintulot na ito ang app na tukuyin ang numero ng telepono at mga ID ng device, kung aktibo man ang isang tawag, at ang malayuang numerong ikinonekta ng isang tawag."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"basahin ang pangunahing status at pagkakakilanlan sa telephony"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Pinapayagan ang app na i-access ang mga pangunahing feature sa telephony ng device."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"iruta ang mga tawag sa pamamagitan ng system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Pinapayagan ang app na iruta ang mga tawag nito sa pamamagitan ng system upang mapahusay ang karanasan sa pagtawag."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"tingnan at kontrolin ang mga tawag sa pamamagitan ng system."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"I-tap para i-delete ang iyong face model, pagkatapos ay idagdag ulit ang mukha mo"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"I-set up ang Pag-unlock Gamit ang Mukha"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"I-unlock ang iyong telepono sa pamamagitan ng pagtingin dito"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para magamit ang Pag-unlock Gamit ang Mukha, i-on ang "<b>"Access sa camera"</b>" sa Mga Setting &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Mag-set up ng higit pang paraan para mag-unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"I-tap para magdagdag ng fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Pag-unlock Gamit ang Fingerprint"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Ihinahanda ang <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Sinisimulan ang apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Pagtatapos ng pag-boot."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"I-off ang screen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Habang sine-set up ang iyong fingerprint, pinindot mo ang Power button.\n\nKaraniwan nitong ino-off ang iyong screen."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"I-off"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Kanselahin"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Ituloy ang pag-set up?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pinindot mo ang power button — karaniwan nitong ino-off ang screen.\n\nSubukang i-tap habang sine-set up ang iyong fingerprint."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"I-off ang screen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Ituloy ang setup"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Magpatuloy sa pag-verify ng fingerprint?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pinindot mo ang power button — karaniwan nitong ino-off ang screen.\n\nSubukang i-tap para i-verify ang iyong fingerprint."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"I-off ang screen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Magpatuloy"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Tumatakbo ang <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Mag-tap upang bumalik sa laro"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pumili ng laro"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index e676c8959aae..bc26fedd0ab9 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Uygulamanın, sizin müdahaleniz olmadan telefon etmek için IMS hizmetini kullanmasına izin verir."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"telefonun durumunu ve kimliğini okuma"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Uygulamaya cihazdaki telefon özelliklerine erişme izni verir. Bu izin, uygulamanın telefon numarasını ve cihaz kimliğini, etkin bir çağrı olup olmadığını ve çağrıda bağlanılan karşı tarafın numarasını öğrenmesine olanak sağlar."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"temel telefon durumunu ve kimliğini okuma"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Uygulamanın, cihazdaki temel telefon özelliklerine erişmesine izin verir."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"aramaları sistem üzerinden yönlendir"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Uygulamanın, çağrı deneyimini iyileştirmek için çağrılarını sistem üzerinden yönlendirmesine olanak tanır."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"aramaları sistemde görüp denetleme."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Yüz modelinizi silmek için dokunup ardından yüzünüzü yeniden ekleyin"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Yüz Tanıma Kilidi\'ni kurma"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefonunuza bakarak kilidini açın"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Yüz Tanıma Kilidi\'ni kullanmak için Ayarlar &gt; Gizlilik bölümünden "<b>"Kamera erişimi"</b>"\'ni açın"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Kilidi açmak için daha fazla yöntem ayarlayın"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Parmak izi eklemek için dokunun"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Parmak İzi Kilidi"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> hazırlanıyor."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Uygulamalar başlatılıyor"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Açılış tamamlanıyor."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ekran kapatılsın mı?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Parmak izinizi ayarlarken Güç düğmesine bastınız.\n\nBu hareket genellikle ekranınızın kapanmasına neden olur."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Kapat"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"İptal"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Kuruluma devam edilsin mi?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Güç düğmesine bastınız. Bu düğmeye basıldığında genellikle ekran kapanır.\n\nParmak izinizi tanımlarken hafifçe dokunmayı deneyin."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Ekranı kapat"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Kuruluma devam et"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Parmak izi doğrulamaya devam edilsin mi?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Güç düğmesine bastınız. Bu düğmeye basıldığında genellikle ekran kapanır.\n\nParmak izinizi doğrulamak için hafifçe dokunmayı deneyin."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ekranı kapat"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Devam"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> çalışıyor"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Oyuna geri dönmek için dokunun"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Oyun seçin"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 18c5600738d1..5ce1407f1737 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Додаток зможе телефонувати за допомогою служби IMS без вашого відома."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"читати статус та ідентифікаційну інформацію телефону"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Дозволяє програмі отримувати доступ до телефонних функцій пристрою. Такий дозвіл дає програмі змогу визначати номер телефону й ідентифікатори пристрою, активність виклику, а також віддалений номер, на який здійснюється виклик."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"зчитувати статус та ідентифікаційну інформацію телефонії"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Дозволяє додатку отримувати доступ до базових функцій телефонії на пристрої."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"маршрутизувати виклики через систему"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Дозволяє додатку маршрутизувати виклики через систему, щоб було зручніше телефонувати."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Переглядати виклики через систему й керувати ними."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Натисніть, щоб видалити свою модель обличчя, а потім знову додайте її"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Налаштування фейсконтролю"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ви зможете розблоковувати телефон, подивившись на нього"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Щоб використовувати фейсконтроль, увімкніть "<b>"Доступ до камери"</b>" в розділі \"Налаштування\" &gt; \"Конфіденційність\""</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Налаштуйте більше способів розблокування"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Натисніть, щоб додати відбиток пальця"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Розблокування відбитком пальця"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Підготовка додатка <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск програм."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Завершення завантаження."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Вимкнути екран?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Налаштовуючи відбиток пальця, ви натиснули кнопку живлення.\n\nЗазвичай після цього вимикається екран."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Вимкнути"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Скасувати"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Продовжити реєстрацію?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Ви натиснули кнопку живлення – зазвичай після цього вимикається екран.\n\nЩоб зареєструвати відбиток пальця, спробуйте лише злегка торкнутися датчика."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Вимкнути екран"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Додати відбиток"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Продовжити підтвердження відбитка?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Ви натиснули кнопку живлення – зазвичай після цього вимикається екран.\n\nЩоб підтвердити відбиток пальця, спробуйте лише злегка торкнутися датчика."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Вимкнути екран"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Продовжити"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Працює <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Торкніться, щоб повернутися в гру"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Виберіть гру"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index fdb8ddd0e1fe..7ceaa992d0d1 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"‏آپ کی مداخلت کے بغیر کالیں کرنے کیلئے ایپ کو IMS سروس استعمال کرنے کی اجازت دیتی ہے۔"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"فون کے اسٹیٹس اور شناخت کو پڑھیں"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"‏ایپ کو آلے کی فون والی خصوصیات تک رسائی حاصل کرنے کی اجازت دیتا ہے۔ یہ اجازت ایپ کو فون نمبر اور آلے کے IDs کا تعین کرنے، آیا کوئی کال فعال ہے، اور کال کے ذریعہ مربوط ریموٹ نمبر کا تعین کرنے دیتی ہے۔"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"بنیادی ٹیلیفونی صورتحال اور شناخت کو پڑھیں"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"اس سے ایپ کو آلے کی بنیادی ٹیلیفونی خصوصیات تک رسائی کی اجازت ملتی ہے۔"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"سسٹم کے ذریعہ کالز روٹ کریں"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"کالںگ کا تجربہ بہتر بنانے کے لیے سسٹم کے ذریعہ ایپ کو کالز روٹ کرنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"سسٹم کے ذریعے کالز دیکھیں اور کنٹرول کریں۔"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"اپنے چہرے کا ماڈل حذف کرنے کے لیے تھپتھپائیں پھر اپنا چہرہ دوبارہ شامل کریں"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"فیس اَنلاک سیٹ اپ کریں"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"اپنے فون کی طرف دیکھ کر اسے غیر مقفل کریں"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"فیس اَنلاک کا استعمال کرنے کے لیے، ترتیبات اور رازداری میں "<b>"کیمرے تک رسائی"</b>" کو آن کریں"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"غیر مقفل کرنے کے مزید طریقے سیٹ اپ کریں"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"فنگر پرنٹ شامل کرنے کیلئے تھپتھپائیں"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"فنگر پرنٹ اَن لاک"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> تیار ہو رہی ہے۔"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ایپس شروع ہو رہی ہیں۔"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"بوٹ مکمل ہو رہا ہے۔"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"اسکرین آف کریں؟"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"اپنی فنگر پرنٹ سیٹ اپ کرنے کے دوران آپ نے پاور بٹن دبایا۔\n\n یہ عام طور پر آپ کی اسکرین کو آف کرتی ہے۔"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"آف کریں"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"منسوخ کریں"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"سیٹ اپ جاری رکھیں؟"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"آپ نے پاور بٹن دبایا — اس سے عام طور پر اسکرین آف ہو جاتی ہے۔\n\nاپنے فنگر پرنٹ کو سیٹ اپ کرنے کے دوران ہلکا سا تھپتھپانے کی کوشش کریں۔"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"اسکرین آف کریں"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"سیٹ اپ جاری رکھیں"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"اپنے فنگر پرنٹ کی توثیق کرنا جاری رکھیں؟"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"آپ نے پاور بٹن دبایا — اس سے عام طور پر اسکرین آف ہو جاتی ہے۔\n\nاپنے فنگر پرنٹ کی توثیق کرنے کے لیے ہلکا سا تھپتھپانے کی کوشش کریں۔"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"اسکرین آف کریں"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"جاری رکھیں"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> چل رہی ہے"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"گیم پر واپس جانے کے لیے تھپتھپائیں"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"گیم کا انتخاب کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 73cc4961bcc2..a40b67f19934 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Ilovaga sizning ishtirokingizsiz qo‘ng‘iroqlarni amalga oshirish uchun IMS xizmatidan foydalanishga ruxsat beradi."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"telefon holati haqidagi ma’lumotlarni olish"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Ilovaga qurilmangizdagi telefon xususiyatlariga kirishga ruxsat beradi. Bu ruxsat ilovaga telefon raqami va qurilma nomlari, qo‘ng‘iroq faol yoki faolsizligi va masofadagi raqam qo‘ng‘rioq orqali bog‘langanligini aniqlashga imkon beradi."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"oddiy telefoniya holatini oʻqish va aniqlash"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Ilovaga qurilmadagi oddiy telefoniya funksiyalaridan foydalanish imkonini beradi."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"chaqiruvlarni tizim orqali yo‘naltirish"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Ilova aloqa sifatini yaxshilash maqsadida chaqiruvlarni tizim orqali yo‘naltirishi mumkin."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"tizim orqali chaqiruvlarni tekshirish va boshqarish."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Yuz modelini oʻchirish uchun bosing va keyin yana yuzni qoʻshing"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Yuz bilan ochishni sozlash"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefoningizni yuz tekshiruvi yordamida qulfdan chiqaring"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Yuz bilan ochish uchun Sozlamalar va maxfiylik orqali "<b>"kameraga kirishga ruxsat bering"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Qulfdan chiqarishning boshqa usullarini sozlang"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Barmoq izi kiritish uchun bosing"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Barmoq izi bilan ochish"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> tayyorlanmoqda."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Ilovalar ishga tushirilmoqda."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Tizimni yuklashni tugatish."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ekran oʻchirilsinmi?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Barmoq izini sozlayotganda Quvvat tugmasini bosdingiz.\n\nBunda odatda ekran oʻchib qoladi."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Oʻchirish"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Bekor qilish"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Sozlashni davom ettirasizmi?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Quvvat tugmasini bosdingiz — bu odatda ekranni oʻchiradi.\n\nBarmoq izini qoʻshish vaqtida tugmaga yengilgina tegining."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Ekranni oʻchirish"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Sozlashni davom ettirish"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Barmoq izi tasdiqlashda davom etilsinmi?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Quvvat tugmasini bosdingiz. Bu odatda ekranni oʻchiradi.\n\nBarmoq izingizni tasdiqlash uchun tugmaga yengilgina tegining."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ekranni oʻchirish"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Davom etish"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ishlamoqda"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"O‘yinga qaytish uchun bosing"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"O‘yinni tanlang"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index d47b4f484e5c..1731b3b2c4db 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Cho phép ứng dụng sử dụng dịch vụ IMS để thực hiện cuộc gọi mà không có sự can thiệp của bạn."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"đọc trạng thái và nhận dạng của điện thoại"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Cho phép ứng dụng truy cập vào các tính năng điện thoại của thiết bị. Quyền này cho phép ứng dụng xác định số điện thoại và ID thiết bị, cho dù cuộc gọi có hiện hoạt hay không và số từ xa có được kết nối bằng một cuộc gọi hay không."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"đọc danh tính và trạng thái cơ bản của điện thoại"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Cho phép ứng dụng dùng các tính năng cơ bản của điện thoại trên thiết bị."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"định tuyến cuộc gọi thông qua hệ thống"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Cho phép ứng dụng định tuyến cuộc gọi thông qua hệ thống nhằm cải thiện trải nghiệm gọi điện."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"xem và kiểm soát cuộc gọi thông qua hệ thống."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Nhấn để xóa mẫu khuôn mặt, sau đó thêm lại khuôn mặt của bạn"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Thiết lập tính năng Mở khóa bằng khuôn mặt"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Mở khóa điện thoại bằng cách nhìn vào điện thoại"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Để dùng tính năng Mở khoá bằng khuôn mặt, hãy bật tuỳ chọn "<b>"Truy cập máy ảnh"</b>" trong phần Cài đặt &gt; Quyền riêng tư"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Thiết lập thêm những cách mở khóa khác"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Nhấn để thêm vân tay"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Mở khóa bằng vân tay"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Đang chuẩn bị <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Khởi động ứng dụng."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Hoàn tất khởi động."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Tắt màn hình?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Trong khi thiết lập vân tay, bạn đã nhấn vào nút Nguồn.\n\nThông thường, thao tác này sẽ tắt màn hình."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Tắt"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Hủy"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Tiếp tục thiết lập?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Bạn đã nhấn nút nguồn – thao tác này thường tắt màn hình.\n\nHãy thử nhấn nhẹ khi thiết lập vân tay của bạn."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Tắt màn hình"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Tiếp tục thiết lập"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Tiếp tục xác minh vân tay của bạn?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Bạn đã nhấn nút nguồn – thao tác này thường tắt màn hình.\n\nHãy thử nhấn nhẹ để xác minh vân tay."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Tắt màn hình"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Tiếp tục"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> đang hoạt động"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Nhấn để quay lại trò chơi"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Chọn trò chơi"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 17ce14b90136..8ce9ab2e4370 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"允许应用自行使用即时通讯服务拨打电话。"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"读取手机状态和身份"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"允许该应用访问设备的电话功能。此权限可让该应用确定本机号码和设备 ID、是否正处于通话状态以及拨打的号码。"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"读取基本电话状态和身份信息"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"允许该应用使用设备的基本电话功能。"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"通过系统转接来电"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"允许该应用通过系统转接来电,以改善通话体验。"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"查看并控制通过系统拨打的电话。"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"请点按以删除您的脸部模型,然后再添加您的脸部模型"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"设置人脸解锁"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"脸部对准手机即可将其解锁"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"如需使用人脸解锁功能,请在“设置”&gt;“隐私权”中开启"<b>"摄像头使用权限"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"设置更多解锁方式"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"点按即可添加指纹"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指纹解锁"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"正在准备升级<xliff:g id="APPNAME">%1$s</xliff:g>。"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在启动应用。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"即将完成启动。"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"要关闭屏幕吗?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"您在设置指纹时按了电源按钮。\n\n此操作通常会关闭屏幕。"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"关闭"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"取消"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"要继续设置吗?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"您已按电源按钮,这通常会关闭屏幕。\n\n请尝试在设置指纹时轻轻按一下。"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"关闭屏幕"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"继续设置"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"要继续验证您的指纹吗?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"您已按电源按钮,这通常会关闭屏幕。\n\n请尝试轻轻按一下来验证您的指纹。"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"关闭屏幕"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"继续"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g>正在运行"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"点按即可返回游戏"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"选择游戏"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 35a11b2eef96..49850e14d91a 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"允許應用程式自行使用 IMS 服務撥打電話。"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"讀取手機狀態和識別碼"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"允許應用程式使用裝置的電話功能。這項權限允許應用程式確定手機號碼和裝置編號、是否正在通話中,以及所撥打的對方號碼。"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"讀取基本電話狀態和身分"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"允許應用程式存取裝置的基本電話功能。"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"透過系統轉接來電"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"允許應用程式透過系統轉接來電,以改善通話體驗。"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"透過系統查看和控制通話。"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"請輕按這裡刪除面部模型,然後再重新新增"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"設定「面孔解鎖」"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"直望手機即可解鎖"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"如要使用「面孔解鎖」,請在 [設定] &gt; [私隱] 開啟"<b>"相機存取權"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"設定更多解鎖方法"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"輕按即可新增指紋"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指紋解鎖"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"正在準備 <xliff:g id="APPNAME">%1$s</xliff:g>。"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在啟動應用程式。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"啟動完成。"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"要關閉螢幕嗎?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"您在設定指紋時按了開關按鈕。\n\n螢幕通常會因此而關閉。"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"關閉"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"取消"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"要繼續設定嗎?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"您已按下開關按鈕,這麼做通常會關閉螢幕。\n\n設定指紋時請嘗試輕按開關按鈕。"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"關閉螢幕"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"繼續設定"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"要繼續驗證指紋嗎?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"您已按下開關按鈕,這麼做通常會關閉螢幕。\n\n嘗試輕按開關按鈕以驗證指紋。"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"關閉螢幕"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"繼續"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"正在執行 <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"輕按即可返回遊戲"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"選擇遊戲"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 42f0586a13af..329b5dc15661 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"允許應用程式自動使用 IMS 服務撥打電話。"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"讀取手機狀態和識別碼"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"允許應用程式使用裝置的電話功能。這項權限可讓應用程式判讀手機號碼和裝置 ID、是否正在通話中,以及所撥打的對方號碼。"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"讀取基本電話通訊狀態和識別資訊"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"允許應用程式存取裝置的基本電話通訊功能。"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"透過系統接通來電"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"允許應用程式透過系統接通來電,以改善通話品質。"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"查看及控管透過系統撥打的電話。"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"請輕觸這裡刪除臉部模型,然後再重新新增"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"設定人臉解鎖功能"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"看著手機就能解鎖"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"如要使用人臉解鎖功能,請前往「設定」&gt;「隱私權」開啟"<b>"攝影機存取權"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"設定更多解鎖方式"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"輕觸即可新增指紋"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指紋解鎖"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"正在準備升級「<xliff:g id="APPNAME">%1$s</xliff:g>」。"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在啟動應用程式。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"啟動完成。"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"要關閉螢幕嗎?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"設定指紋時必須按下電源鍵。\n\n這項操作通常會將螢幕關閉。"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"關閉"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"取消"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"要繼續設定嗎?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"你已按下電源鍵,這麼做通常會關閉螢幕。\n\n設定指紋時請試著減輕觸碰電源鍵的力道。"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"關閉螢幕"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"繼續設定"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"要繼續驗證指紋嗎?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"你已按下電源鍵,這麼做通常會關閉螢幕。\n\n驗證指紋時,請試著減輕觸碰電源鍵的力道。"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"關閉螢幕"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"繼續"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> 執行中"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"輕觸即可返回遊戲"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"選擇遊戲"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 3f7e37d4ea5d..8c7d5ea1a6bc 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Ivumela uhlelo lokusebenza ukuthi lusebenzise isevisi ye-IMS ukuze yenze amakholi ngaphandle kokungenelela kwakho."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"funda isimo sefoni kanye nesazisi"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Ivumela uhlelo lokusebenza ukufinyelela izici zefoni zedivayisi. Le mvume ivumela uhlelo lokusebenza ukucacisa inombolo yefoni nobunikazi bedivayisi, ukuthi noma ikholi iyasebenza, futhi nenombolo yesilawuli kude zixhunywe ngekholi."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"funda isimo nokuhlonza ucingo okuyisisekelo"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Ivumela i-app ukufinyelela izakhi zocingo eziyisisekelo zedivayisi."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"yanza imizila yamakholi ngesistimu"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Ivumela uhlelo lokusebenza ukwenza imizila yamakholi ngesistimu ukuze ithuthukise umuzwa wokushaya."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"bona futhi ulawule amakholi ngesistimu."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Thepha ukuze usule imodeli yakho yobuso, bese wengeza futhi ubuso"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Setha Ukuvula ngobuso"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Vula ifoni yakho ngokuyibheka"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Ukuze usebenzise Ukuvula ngobuso, vula "<b>"Ukufinyelela kwekhamera"</b>" kokuthi Amasethingi &gt; Ubumfihlo"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Setha izindlela eziningi zokuvula"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Thepha ukuze ungeze izigxivizo zomunwe"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Ukuvula ngesigxivizo somunwe"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Ukulungisela i-<xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Qalisa izinhlelo zokusebenza."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Qedela ukuqala kabusha."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vala isikrini?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Lapho usetha izigxivizo zeminwe yakho, ucindezele Inkinobho yamandla.\n\nLokhu kuvame ukuvala isikrini sakho."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Vala"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Khansela"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Qhubeka nokusetha?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Ucindezele inkinobho yamandla — lokhu kuvame ukuvala isikrini.\n\nZama ukuthepha kancane ngenkathi usetha isigxivizo sakho somunwe."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Vala isikrini"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Qhubeka nokusetha"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Qhubeka uqinisekise isigxivizo sakho somunwe?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Ucindezele inkinobho yamandla — lokhu kuvame ukuvala isikrini.\n\nZama ukuthepha kancane ukuze uqinisekise isigxivizo sakho somunwe."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Vala isikrini"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Qhubeka"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> iyasebenza"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Thepha ukuze ubuyele emuva kwigeyimu"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Khetha igeyimu"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 60766459f1cb..bfc1c83e8fc5 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3638,6 +3638,9 @@
to re-retrieve all resources (including view layouts, drawables, etc)
to correctly handle any configuration change.-->
<attr name="configChanges" />
+ <!-- Specifies whether the IME supports Handwriting using stylus. Defaults to false. -->
+ <attr name="supportsStylusHandwriting" format="boolean" />
+
</declare-styleable>
<!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and
@@ -8775,6 +8778,14 @@
<attr name="hotwordDetectionService" format="string" />
</declare-styleable>
+ <!-- Use <code>game-service</code> as the root tag of the XML resource that
+ describes a GameService.
+ Described here are the attributes that can be included in that tag. -->
+ <declare-styleable name="GameService">
+ <!-- The service that hosts active game sessions. This is required. -->
+ <attr name="gameSessionService" format="string" />
+ </declare-styleable>
+
<!-- Use <code>voice-enrollment-application</code>
as the root tag of the XML resource that escribes the supported keyphrases (hotwords)
by the enrollment application.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4feee41533be..bd0604e03fee 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3333,6 +3333,20 @@
and one pSIM) -->
<integer name="config_num_physical_slots">1</integer>
+ <!--The default "usage setting" indicating that the device is either a voice-centric
+ device (1) or a data-centric device (2). A voice-centric device will require that any cellular
+ service that it uses provides access to voice capability, and a data-centric device will
+ likewise require that the network provides access to data services. These settings are
+ sent to the cellular modem and control the behavior in accordance with 3gpp TS 24.301 sec 4.3
+ (and equivalent functionality in other generations of cellular).-->
+ <integer name="config_default_cellular_usage_setting">1</integer>
+
+ <!--The list of supported cellular usage settings for this device.-->
+ <integer-array translatable="false" name="config_supported_cellular_usage_settings">
+ <item>1</item> <!-- USAGE_SETTING_VOICE_CENTRIC -->
+ <item>2</item> <!-- USAGE_SETTING_DATA_CENTRIC -->
+ </integer-array>
+
<!-- When a radio power off request is received, we will delay completing the request until
either IMS moves to the deregistered state or the timeout defined by this configuration
elapses. If 0, this feature is disabled and we do not delay radio power off requests.-->
@@ -5054,6 +5068,10 @@
If given value is outside of this range, the option 1 (center) is assummed. -->
<integer name="config_letterboxDefaultPositionForReachability">1</integer>
+ <!-- Whether a camera compat controller is enabled to allow the user to apply or revert
+ treatment for stretched issues in camera viewfinder. -->
+ <bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool>
+
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
@@ -5523,4 +5541,16 @@
</string-array>
<integer name="config_chooser_max_targets_per_row">4</integer>
+
+ <!-- Package that provides the supervised user creation flow. This package must include an
+ activity with an intent filter for {@link UserManager.ACTION_CREATE_SUPERVISED_USER}.
+ When this resource is defined, an extra button in user settings screen will be shown
+ with a title defined in @*android:string/supervised_user_creation_label
+ and an icon defined in @*android:drawable/ic_add_supervised_user.
+ That button will fire an intent targeted for this package with the mentioned action.
+ When this resource is empty, that button will not be shown. -->
+ <string name="config_supervisedUserCreationPackage" translatable="false"></string>
+
+ <!-- Determines whether SafetyCenter feature is enabled. -->
+ <bool name="config_enableSafetyCenter">true</bool>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b9c75649b6ff..bc127d9c1d06 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3249,6 +3249,9 @@
<public name="canDisplayOnRemoteDevices" />
<public name="supportedTypes" />
<public name="resetEnabledSettingsOnAppDataCleared" />
+ <public name="supportsStylusHandwriting" />
+ <!-- @hide @SystemApi -->
+ <public name="gameSessionService" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 769e667a2b25..b16e462192f5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5374,6 +5374,8 @@
<string name="user_creation_account_exists">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> (a User with this account already exists) ?</string>
<!-- Message to user that app is trying to create user for a specified account. [CHAR LIMIT=none] -->
<string name="user_creation_adding">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> ?</string>
+ <!-- String label displayed on buttons that trigger the flow for creating supervised users. [CHAR LIMIT=35] -->
+ <string name="supervised_user_creation_label">Add supervised user</string>
<!-- Locale picker strings -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 55bf24b25b0d..7687b93cf760 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -492,6 +492,8 @@
<java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" />
<java-symbol type="string" name="config_deviceSpecificAudioService" />
<java-symbol type="integer" name="config_num_physical_slots" />
+ <java-symbol type="integer" name="config_default_cellular_usage_setting" />
+ <java-symbol type="array" name="config_supported_cellular_usage_settings" />
<java-symbol type="integer" name="config_delay_for_ims_dereg_millis" />
<java-symbol type="array" name="config_integrityRuleProviderPackages" />
<java-symbol type="bool" name="config_useAssistantVolume" />
@@ -4292,6 +4294,7 @@
<java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" />
<java-symbol type="bool" name="config_letterboxIsReachabilityEnabled" />
<java-symbol type="integer" name="config_letterboxDefaultPositionForReachability" />
+ <java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
@@ -4629,4 +4632,8 @@
<java-symbol type="integer" name="config_mashPressVibrateTimeOnPowerButton" />
<java-symbol type="string" name="config_systemGameService" />
+
+ <java-symbol type="string" name="config_supervisedUserCreationPackage"/>
+
+ <java-symbol type="bool" name="config_enableSafetyCenter" />
</resources>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 32d72b37b8af..c18a70c5dd2a 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -138,3 +138,39 @@ java_genrule {
"done && " +
"$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
}
+
+// In addition to running as part of FrameworksCoreTests, we run (a subclass of)
+// ChooserActivityTest against the unbundled ChooserActivity implementation in
+// //packages/modules/IntentResolver/. The following library provides the
+// minimum dependencies required to build that test in the unbundled package.
+android_library {
+ name: "ChooserActivityTestsLib",
+ visibility: ["//packages/modules/IntentResolver/java/tests:__pkg__"],
+
+ srcs: [
+ "src/com/android/internal/app/ChooserActivityLoggerFake.java",
+ "src/com/android/internal/app/ChooserActivityOverrideData.java",
+ "src/com/android/internal/app/ChooserActivityTest.java",
+ "src/com/android/internal/app/ChooserWrapperActivity.java",
+ "src/com/android/internal/app/IChooserWrapper.java",
+ "src/com/android/internal/app/MatcherUtils.java",
+ "src/com/android/internal/app/ResolverDataProvider.java",
+ ],
+
+ static_libs: [
+ "androidx.test.espresso.core",
+ "androidx.test.ext.junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "truth-prebuilt",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ "framework",
+ "framework-res",
+ ],
+} \ No newline at end of file
diff --git a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
index d936cad15689..121caef87f6f 100644
--- a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
+++ b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
@@ -16,6 +16,10 @@
package android.net
+import android.net.NetworkTemplate.MATCH_BLUETOOTH
+import android.net.NetworkTemplate.MATCH_ETHERNET
+import android.net.NetworkTemplate.MATCH_MOBILE
+import android.net.NetworkTemplate.MATCH_WIFI
import android.text.format.Time.TIMEZONE_UTC
import androidx.test.runner.AndroidJUnit4
import org.junit.Test
@@ -24,6 +28,8 @@ import java.io.ByteArrayInputStream
import java.io.DataInputStream
import java.time.ZoneId
import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
private const val TEST_IMSI1 = "TESTIMSI1"
private const val TEST_SSID1 = "TESTISSID1"
@@ -53,4 +59,26 @@ class NetworkPolicyTest {
val restored = NetworkPolicy.getNetworkPolicyFromBackup(stream)
assertEquals(policy, restored)
}
+
+ @Test
+ fun testIsTemplatePersistable() {
+ listOf(MATCH_MOBILE, MATCH_WIFI).forEach {
+ // Verify wildcard templates cannot be persistable.
+ assertFalse(NetworkPolicy.isTemplatePersistable(NetworkTemplate.Builder(it).build()))
+
+ // Verify mobile/wifi templates can be persistable if the Subscriber Id is supplied.
+ assertTrue(NetworkPolicy.isTemplatePersistable(NetworkTemplate.Builder(it)
+ .setSubscriberIds(setOf(TEST_IMSI1)).build()))
+ }
+
+ // Verify bluetooth and ethernet templates can be persistable without any other
+ // field is supplied.
+ listOf(MATCH_BLUETOOTH, MATCH_ETHERNET).forEach {
+ assertTrue(NetworkPolicy.isTemplatePersistable(NetworkTemplate.Builder(it).build()))
+ }
+
+ // Verify wifi template can be persistable if the Wifi Network Key is supplied.
+ assertTrue(NetworkPolicy.isTemplatePersistable(NetworkTemplate.Builder(MATCH_WIFI)
+ .setWifiNetworkKey(TEST_SSID1).build()))
+ }
} \ No newline at end of file
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
index 16dcff5e0c8c..7ccbb0150cb6 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
@@ -16,6 +16,10 @@
package android.os.storage;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.content.res.Resources;
import android.test.InstrumentationTestCase;
@@ -23,6 +27,10 @@ import android.util.Log;
import libcore.io.Streams;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
@@ -39,6 +47,7 @@ public class StorageManagerBaseTest extends InstrumentationTestCase {
protected Context mContext = null;
protected StorageManager mSm = null;
+ @Mock private File mFile;
private static String LOG_TAG = "StorageManagerBaseTest";
protected static final long MAX_WAIT_TIME = 120*1000;
protected static final long WAIT_TIME_INCR = 5*1000;
@@ -121,12 +130,50 @@ public class StorageManagerBaseTest extends InstrumentationTestCase {
*/
@Override
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
mContext = getInstrumentation().getContext();
mSm = (StorageManager)mContext.getSystemService(android.content.Context.STORAGE_SERVICE);
}
/**
+ * Tests the space reserved for cache when system has high free space i.e. more than
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total space.
+ */
+ @Test
+ public void testGetStorageCacheBytesUnderHighStorage() throws Exception {
+ when(mFile.getUsableSpace()).thenReturn(10000L);
+ when(mFile.getTotalSpace()).thenReturn(15000L);
+ long result = mSm.getStorageCacheBytes(mFile, 0);
+ assertThat(result).isEqualTo(1500L);
+ }
+
+ /**
+ * Tests the space reserved for cache when system has low free space i.e. less than
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_LOW of total space.
+ */
+ @Test
+ public void testGetStorageCacheBytesUnderLowStorage() throws Exception {
+ when(mFile.getUsableSpace()).thenReturn(10000L);
+ when(mFile.getTotalSpace()).thenReturn(250000L);
+ long result = mSm.getStorageCacheBytes(mFile, 0);
+ assertThat(result).isEqualTo(5000L);
+ }
+
+ /**
+ * Tests the space reserved for cache when system has moderate free space i.e.more than
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_LOW of total space but less than
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total space.
+ */
+ @Test
+ public void testGetStorageCacheBytesUnderModerateStorage() throws Exception {
+ when(mFile.getUsableSpace()).thenReturn(10000L);
+ when(mFile.getTotalSpace()).thenReturn(100000L);
+ long result = mSm.getStorageCacheBytes(mFile, 0);
+ assertThat(result).isEqualTo(4666L);
+ }
+
+ /**
* Creates an OBB file (with the given name), into the app's standard files directory
*
* @param name The name of the OBB file we want to create/write to
@@ -526,4 +573,4 @@ public class StorageManagerBaseTest extends InstrumentationTestCase {
doValidateIntContents(path + File.separator + "subdir2" + File.separator + "subdir2a"
+ File.separator + "subdir2a1", "OneToOneThousandInts", 0, 1000);
}
-} \ No newline at end of file
+}
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 78a8f7b3f32e..c4c983d24af9 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.InputDevice.SOURCE_CLASS_POINTER;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.TOOL_TYPE_FINGER;
@@ -214,4 +215,27 @@ public class MotionEventTest {
rotInvalid.transform(mat);
assertEquals(-1, rotInvalid.getSurfaceRotation());
}
+
+ @Test
+ public void testUsesPointerSourceByDefault() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 0 /* x */, 0 /* y */, 0 /* metaState */);
+ assertTrue(event.isFromSource(SOURCE_CLASS_POINTER));
+ }
+
+ @Test
+ public void testLocationOffsetOnlyAppliedToNonPointerSources() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 10 /* x */, 20 /* y */, 0 /* metaState */);
+ event.offsetLocation(40, 50);
+
+ // The offset should be applied since a pointer source is used by default.
+ assertEquals(50, (int) event.getX());
+ assertEquals(70, (int) event.getY());
+
+ // The offset should not be applied if the source is changed to a non-pointer source.
+ event.setSource(InputDevice.SOURCE_JOYSTICK);
+ assertEquals(10, (int) event.getX());
+ assertEquals(20, (int) event.getY());
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java
new file mode 100644
index 000000000000..499f7a55996b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.os.UserHandle;
+
+import com.android.internal.app.chooser.TargetInfo;
+import com.android.internal.logging.MetricsLogger;
+
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Singleton providing overrides to be applied by any {@code IChooserWrapper} used in testing.
+ * We cannot directly mock the activity created since instrumentation creates it, so instead we use
+ * this singleton to modify behavior.
+ */
+public class ChooserActivityOverrideData {
+ private static ChooserActivityOverrideData sInstance = null;
+
+ public static ChooserActivityOverrideData getInstance() {
+ if (sInstance == null) {
+ sInstance = new ChooserActivityOverrideData();
+ }
+ return sInstance;
+ }
+
+ @SuppressWarnings("Since15")
+ public Function<PackageManager, PackageManager> createPackageManager;
+ public Function<TargetInfo, Boolean> onSafelyStartCallback;
+ public Function<ChooserListAdapter, Void> onQueryDirectShareTargets;
+ public ResolverListController resolverListController;
+ public ResolverListController workResolverListController;
+ public Boolean isVoiceInteraction;
+ public boolean isImageType;
+ public Cursor resolverCursor;
+ public boolean resolverForceException;
+ public Bitmap previewThumbnail;
+ public MetricsLogger metricsLogger;
+ public ChooserActivityLogger chooserActivityLogger;
+ public int alternateProfileSetting;
+ public Resources resources;
+ public UserHandle workProfileUserHandle;
+ public boolean hasCrossProfileIntents;
+ public boolean isQuietModeEnabled;
+ public boolean isWorkProfileUserRunning;
+ public boolean isWorkProfileUserUnlocked;
+ public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
+ public PackageManager packageManager;
+
+ public void reset() {
+ onSafelyStartCallback = null;
+ onQueryDirectShareTargets = null;
+ isVoiceInteraction = null;
+ createPackageManager = null;
+ previewThumbnail = null;
+ isImageType = false;
+ resolverCursor = null;
+ resolverForceException = false;
+ resolverListController = mock(ResolverListController.class);
+ workResolverListController = mock(ResolverListController.class);
+ metricsLogger = mock(MetricsLogger.class);
+ chooserActivityLogger = new ChooserActivityLoggerFake();
+ alternateProfileSetting = 0;
+ resources = null;
+ workProfileUserHandle = null;
+ hasCrossProfileIntents = true;
+ isQuietModeEnabled = false;
+ isWorkProfileUserRunning = true;
+ isWorkProfileUserUnlocked = true;
+ packageManager = null;
+ multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
+ @Override
+ public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
+ int targetUserId) {
+ return hasCrossProfileIntents;
+ }
+
+ @Override
+ public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+ return isQuietModeEnabled;
+ }
+
+ @Override
+ public void requestQuietModeEnabled(boolean enabled,
+ UserHandle workProfileUserHandle) {
+ isQuietModeEnabled = enabled;
+ }
+ };
+ }
+
+ private ChooserActivityOverrideData() {}
+}
+
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 45504c0b4a34..8ed3fac85cb2 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -34,7 +34,6 @@ import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FRO
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;
import static com.android.internal.app.ChooserListAdapter.CALLER_TARGET_SCORE_BOOST;
import static com.android.internal.app.ChooserListAdapter.SHORTCUT_TARGET_SCORE_BOOST;
-import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
import static com.android.internal.app.MatcherUtils.first;
import static junit.framework.Assert.assertFalse;
@@ -46,6 +45,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -81,11 +81,12 @@ import android.net.Uri;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.service.chooser.ChooserTarget;
+import android.view.View;
+import androidx.annotation.CallSuper;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
-import com.android.internal.R;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -93,6 +94,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
+import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -111,11 +113,29 @@ import java.util.Map;
import java.util.function.Function;
/**
- * Chooser activity instrumentation tests
+ * Instrumentation tests for chooser activities that derive from the system
+ * {@code com.android.internal.ChooserActivity}. This class is used directly to test the system
+ * implementation, but clients can inherit from this test to apply the same suite of chooser tests
+ * to their own ChooserActivity implementations. Clients should override
+ * #getConcreteIntentForLaunch() to configure an intent that will launch their concrete
+ * ChooserActivity subtype. Tests will assume that this subtype implements the IChooserWrapper
+ * interface, which is only appropriate for testing. Clients will typically create their own
+ * "ChooserWrapperActivity" by copy-and-pasting the system implementation, parenting to their own
+ * ChooserActivity subclass instead of directly to the system implementation. Code comments in this
+ * file provide direction for developers creating derived test suites, and eventually for removing
+ * the extra complexity once we no longer need to support parallel ChooserActivity implementations.
*/
@RunWith(Parameterized.class)
public class ChooserActivityTest {
+ /* --------
+ * Subclasses should copy the following section verbatim (or alternatively could specify some
+ * additional @Parameterized.Parameters, as long as the correct parameters are used to
+ * initialize the ChooserActivityTest). The subclasses should also be @RunWith the
+ * `Parameterized` runner.
+ * --------
+ */
+
private static final Function<PackageManager, PackageManager> DEFAULT_PM = pm -> pm;
private static final Function<PackageManager, PackageManager> NO_APP_PREDICTION_SERVICE_PM =
pm -> {
@@ -132,6 +152,66 @@ public class ChooserActivityTest {
});
}
+ /* --------
+ * Subclasses can override the following methods to customize test behavior.
+ * --------
+ */
+
+ /**
+ * Perform any necessary per-test initialization steps (subclasses may add additional steps
+ * before and/or after calling up to the superclass implementation).
+ */
+ @CallSuper
+ protected void setup() {
+ cleanOverrideData();
+ }
+
+ /**
+ * Given an intent that was constructed in a test, perform any additional configuration to
+ * specify the appropriate concrete ChooserActivity subclass. The activity launched by this
+ * intent must descend from android.internal.app.ChooserActivity (for our ActivityTestRule), and
+ * must also implement the android.internal.app.IChooserWrapper interface (since test code will
+ * assume the ability to make unsafe downcasts).
+ */
+ protected Intent getConcreteIntentForLaunch(Intent clientIntent) {
+ clientIntent.setClass(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(),
+ com.android.internal.app.ChooserWrapperActivity.class);
+ return clientIntent;
+ }
+
+ /* --------
+ * The code in this section is unorthodox and can be simplified/reverted when we no longer need
+ * to support the parallel chooser implementations.
+ * --------
+ */
+
+ // Shared test code references the activity under test as ChooserActivity, the common ancestor
+ // of any (inheritance-based) chooser implementation. For testing purposes, that activity will
+ // usually be cast to IChooserWrapper to expose instrumentation.
+ @Rule
+ public ActivityTestRule<ChooserActivity> mActivityRule =
+ new ActivityTestRule<>(ChooserActivity.class, false, false) {
+ @Override
+ public ChooserActivity launchActivity(Intent clientIntent) {
+ return super.launchActivity(getConcreteIntentForLaunch(clientIntent));
+ }
+ };
+
+ @Before
+ public final void doPolymorphicSetup() {
+ // The base class needs a @Before-annotated setup for when it runs against the system
+ // chooser, while subclasses need to be able to specify their own setup behavior. Notably
+ // the unbundled chooser, running in user-space, needs to take additional steps before it
+ // can run #cleanOverrideData() (which writes to DeviceConfig).
+ setup();
+ }
+
+ /* --------
+ * Subclasses can ignore the remaining code and inherit the full suite of tests.
+ * --------
+ */
+
private static final String TEST_MIME_TYPE = "application/TestType";
private static final int CONTENT_PREVIEW_IMAGE = 1;
@@ -140,10 +220,6 @@ public class ChooserActivityTest {
private Function<PackageManager, PackageManager> mPackageManagerOverride;
private int mTestNum;
- @Rule
- public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
- new ActivityTestRule<>(ChooserWrapperActivity.class, false,
- false);
public ChooserActivityTest(
int testNum,
@@ -153,10 +229,9 @@ public class ChooserActivityTest {
mTestNum = testNum;
}
- @Before
public void cleanOverrideData() {
- sOverrides.reset();
- sOverrides.createPackageManager = mPackageManagerOverride;
+ ChooserActivityOverrideData.getInstance().reset();
+ ChooserActivityOverrideData.getInstance().createPackageManager = mPackageManagerOverride;
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
Boolean.toString(true),
@@ -168,16 +243,22 @@ public class ChooserActivityTest {
Intent viewIntent = createViewTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule.launchActivity(
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
+ final IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(
Intent.createChooser(viewIntent, "chooser test"));
waitForIdle();
assertThat(activity.getAdapter().getCount(), is(2));
assertThat(activity.getAdapter().getServiceTargetCount(), is(0));
- onView(withId(R.id.title)).check(matches(withText("chooser test")));
+ onView(withIdFromRuntimeResource("title")).check(matches(withText("chooser test")));
}
@Test
@@ -185,12 +266,19 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "chooser test"));
waitForIdle();
- onView(withId(R.id.title)).check(matches(withText(R.string.whichSendApplication)));
+ onView(withIdFromRuntimeResource("title"))
+ .check(matches(withTextFromRuntimeResource("whichSendApplication")));
}
@Test
@@ -198,13 +286,19 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.title))
- .check(matches(withText(R.string.whichSendApplication)));
+ onView(withIdFromRuntimeResource("title"))
+ .check(matches(withTextFromRuntimeResource("whichSendApplication")));
}
@Test
@@ -212,13 +306,21 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntentWithPreview(null, null);
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.content_preview_title)).check(matches(not(isDisplayed())));
- onView(withId(R.id.content_preview_thumbnail)).check(matches(not(isDisplayed())));
+ onView(withIdFromRuntimeResource("content_preview_title"))
+ .check(matches(not(isDisplayed())));
+ onView(withIdFromRuntimeResource("content_preview_thumbnail"))
+ .check(matches(not(isDisplayed())));
}
@Test
@@ -227,14 +329,23 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntentWithPreview(previewTitle, null);
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.content_preview_title)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_title)).check(matches(withText(previewTitle)));
- onView(withId(R.id.content_preview_thumbnail)).check(matches(not(isDisplayed())));
+ onView(withIdFromRuntimeResource("content_preview_title"))
+ .check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_title"))
+ .check(matches(withText(previewTitle)));
+ onView(withIdFromRuntimeResource("content_preview_thumbnail"))
+ .check(matches(not(isDisplayed())));
}
@Test
@@ -244,13 +355,20 @@ public class ChooserActivityTest {
Uri.parse("tel:(+49)12345789"));
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.content_preview_title)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_thumbnail)).check(matches(not(isDisplayed())));
+ onView(withIdFromRuntimeResource("content_preview_title")).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_thumbnail"))
+ .check(matches(not(isDisplayed())));
}
@Test
@@ -259,16 +377,23 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntentWithPreview(previewTitle,
Uri.parse("android.resource://com.android.frameworks.coretests/"
+ com.android.frameworks.coretests.R.drawable.test320x240));
- sOverrides.previewThumbnail = createBitmap();
+ ChooserActivityOverrideData.getInstance().previewThumbnail = createBitmap();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.content_preview_title)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_thumbnail)).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_title")).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_thumbnail"))
+ .check(matches(isDisplayed()));
}
@Test @Ignore
@@ -276,19 +401,25 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
assertThat(activity.getAdapter().getCount(), is(2));
- onView(withId(R.id.profile_button)).check(doesNotExist());
+ onView(withIdFromRuntimeResource("profile_button")).check(doesNotExist());
ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
@@ -324,19 +455,25 @@ public class ChooserActivityTest {
}
resolvedComponentInfos.addAll(infosToStack);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
// expect 1 unique targets + 1 group + 4 ranked app targets
assertThat(activity.getAdapter().getCount(), is(6));
ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
@@ -358,27 +495,33 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
UsageStatsManager usm = activity.getUsageStatsManager();
- verify(sOverrides.resolverListController, times(1))
+ verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1))
.topK(any(List.class), anyInt());
assertThat(activity.getIsSelected(), is(false));
- sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
return true;
};
ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
onView(withText(toChoose.activityInfo.name))
.perform(click());
waitForIdle();
- verify(sOverrides.resolverListController, times(1))
+ verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1))
.updateChooserCounts(Mockito.anyString(), anyInt(), Mockito.anyString());
- verify(sOverrides.resolverListController, times(1))
+ verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1))
.updateModel(toChoose.activityInfo.getComponentName());
assertThat(activity.getIsSelected(), is(true));
}
@@ -386,19 +529,27 @@ public class ChooserActivityTest {
@Ignore // b/148158199
@Test
public void noResultsFromPackageManager() {
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(null);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(null);
Intent sendIntent = createSendTextIntent();
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final ChooserActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper wrapper = (IChooserWrapper) activity;
+
waitForIdle();
assertThat(activity.isFinishing(), is(false));
- onView(withId(R.id.empty)).check(matches(isDisplayed()));
- onView(withId(R.id.profile_pager)).check(matches(not(isDisplayed())));
+ onView(withIdFromRuntimeResource("empty")).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("profile_pager")).check(matches(not(isDisplayed())));
InstrumentationRegistry.getInstrumentation().runOnMainSync(
- () -> activity.getAdapter().handlePackagesChanged()
+ () -> wrapper.getAdapter().handlePackagesChanged()
);
// backward compatibility. looks like we finish when data is empty after package change
assertThat(activity.isFinishing(), is(true));
@@ -407,19 +558,25 @@ public class ChooserActivityTest {
@Test
public void autoLaunchSingleResult() throws InterruptedException {
ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final ChooserActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
assertThat(chosen[0], is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
@@ -438,15 +595,15 @@ public class ChooserActivityTest {
ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
Intent sendIntent = createSendTextIntent();
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
// The other entry is filtered to the other profile slot
assertThat(activity.getAdapter().getCount(), is(1));
ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserWrapperActivity.sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
@@ -473,22 +630,22 @@ public class ChooserActivityTest {
createResolvedComponentsForTestWithOtherProfile(3);
ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
- when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+ when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
- when(ChooserWrapperActivity.sOverrides.resolverListController.getLastChosen())
+ when(ChooserActivityOverrideData.getInstance().resolverListController.getLastChosen())
.thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
// The other entry is filtered to the other profile slot
assertThat(activity.getAdapter().getCount(), is(2));
ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserWrapperActivity.sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
@@ -512,20 +669,20 @@ public class ChooserActivityTest {
createResolvedComponentsForTestWithOtherProfile(3);
ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
- when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+ when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
// The other entry is filtered to the last used slot
assertThat(activity.getAdapter().getCount(), is(2));
ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserWrapperActivity.sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
@@ -544,17 +701,17 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+ when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final ChooserActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.chooser_copy_button)).check(matches(isDisplayed()));
- onView(withId(R.id.chooser_copy_button)).perform(click());
+ onView(withIdFromRuntimeResource("chooser_copy_button")).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("chooser_copy_button")).perform(click());
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(
Context.CLIPBOARD_SERVICE);
ClipData clipData = clipboard.getPrimaryClip();
@@ -571,20 +728,19 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+ when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
- MetricsLogger mockLogger = sOverrides.metricsLogger;
+ MetricsLogger mockLogger = ChooserActivityOverrideData.getInstance().metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.chooser_copy_button)).check(matches(isDisplayed()));
- onView(withId(R.id.chooser_copy_button)).perform(click());
+ onView(withIdFromRuntimeResource("chooser_copy_button")).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("chooser_copy_button")).perform(click());
verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture());
@@ -600,17 +756,17 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+ when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.chooser_nearby_button)).check(matches(isDisplayed()));
- onView(withId(R.id.chooser_nearby_button)).perform(click());
+ onView(withIdFromRuntimeResource("chooser_nearby_button")).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("chooser_nearby_button")).perform(click());
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
@@ -664,22 +820,22 @@ public class ChooserActivityTest {
Uri.parse("android.resource://com.android.frameworks.coretests/"
+ com.android.frameworks.coretests.R.drawable.test320x240));
- sOverrides.previewThumbnail = createBitmap();
- sOverrides.isImageType = true;
+ ChooserActivityOverrideData.getInstance().previewThumbnail = createBitmap();
+ ChooserActivityOverrideData.getInstance().isImageType = true;
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+ when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.chooser_edit_button)).check(matches(isDisplayed()));
- onView(withId(R.id.chooser_edit_button)).perform(click());
+ onView(withIdFromRuntimeResource("chooser_edit_button")).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("chooser_edit_button")).perform(click());
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
@@ -735,20 +891,30 @@ public class ChooserActivityTest {
uris.add(uri);
Intent sendIntent = createSendUriIntentWithPreview(uris);
- sOverrides.previewThumbnail = createBitmap();
- sOverrides.isImageType = true;
+ ChooserActivityOverrideData.getInstance().previewThumbnail = createBitmap();
+ ChooserActivityOverrideData.getInstance().isImageType = true;
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.content_preview_image_1_large)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_image_2_large)).check(matches(not(isDisplayed())));
- onView(withId(R.id.content_preview_image_2_small)).check(matches(not(isDisplayed())));
- onView(withId(R.id.content_preview_image_3_small)).check(matches(not(isDisplayed())));
+ onView(withIdFromRuntimeResource("content_preview_image_1_large"))
+ .check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_image_2_large"))
+ .check(matches(not(isDisplayed())));
+ onView(withIdFromRuntimeResource("content_preview_image_2_small"))
+ .check(matches(not(isDisplayed())));
+ onView(withIdFromRuntimeResource("content_preview_image_3_small"))
+ .check(matches(not(isDisplayed())));
}
@Test
@@ -761,20 +927,30 @@ public class ChooserActivityTest {
uris.add(uri);
Intent sendIntent = createSendUriIntentWithPreview(uris);
- sOverrides.previewThumbnail = createBitmap();
- sOverrides.isImageType = true;
+ ChooserActivityOverrideData.getInstance().previewThumbnail = createBitmap();
+ ChooserActivityOverrideData.getInstance().isImageType = true;
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.content_preview_image_1_large)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_image_2_large)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_image_2_small)).check(matches(not(isDisplayed())));
- onView(withId(R.id.content_preview_image_3_small)).check(matches(not(isDisplayed())));
+ onView(withIdFromRuntimeResource("content_preview_image_1_large"))
+ .check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_image_2_large"))
+ .check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_image_2_small"))
+ .check(matches(not(isDisplayed())));
+ onView(withIdFromRuntimeResource("content_preview_image_3_small"))
+ .check(matches(not(isDisplayed())));
}
@Test
@@ -790,20 +966,30 @@ public class ChooserActivityTest {
uris.add(uri);
Intent sendIntent = createSendUriIntentWithPreview(uris);
- sOverrides.previewThumbnail = createBitmap();
- sOverrides.isImageType = true;
+ ChooserActivityOverrideData.getInstance().previewThumbnail = createBitmap();
+ ChooserActivityOverrideData.getInstance().isImageType = true;
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.content_preview_image_1_large)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_image_2_large)).check(matches(not(isDisplayed())));
- onView(withId(R.id.content_preview_image_2_small)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_image_3_small)).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_image_1_large"))
+ .check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_image_2_large"))
+ .check(matches(not(isDisplayed())));
+ onView(withIdFromRuntimeResource("content_preview_image_2_small"))
+ .check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_image_3_small"))
+ .check(matches(isDisplayed()));
}
@Test
@@ -811,7 +997,7 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
- MetricsLogger mockLogger = sOverrides.metricsLogger;
+ MetricsLogger mockLogger = ChooserActivityOverrideData.getInstance().metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test"));
waitForIdle();
@@ -836,8 +1022,9 @@ public class ChooserActivityTest {
public void testOnCreateLoggingFromWorkProfile() {
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
- sOverrides.alternateProfileSetting = MetricsEvent.MANAGED_PROFILE;
- MetricsLogger mockLogger = sOverrides.metricsLogger;
+ ChooserActivityOverrideData.getInstance().alternateProfileSetting =
+ MetricsEvent.MANAGED_PROFILE;
+ MetricsLogger mockLogger = ChooserActivityOverrideData.getInstance().metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test"));
waitForIdle();
@@ -862,7 +1049,7 @@ public class ChooserActivityTest {
public void testEmptyPreviewLogging() {
Intent sendIntent = createSendTextIntentWithPreview(null, null);
- MetricsLogger mockLogger = sOverrides.metricsLogger;
+ MetricsLogger mockLogger = ChooserActivityOverrideData.getInstance().metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "empty preview logger test"));
waitForIdle();
@@ -877,12 +1064,12 @@ public class ChooserActivityTest {
public void testTitlePreviewLogging() {
Intent sendIntent = createSendTextIntentWithPreview("TestTitle", null);
- MetricsLogger mockLogger = sOverrides.metricsLogger;
+ MetricsLogger mockLogger = ChooserActivityOverrideData.getInstance().metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+ when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
@@ -906,16 +1093,22 @@ public class ChooserActivityTest {
uris.add(uri);
Intent sendIntent = createSendUriIntentWithPreview(uris);
- sOverrides.previewThumbnail = createBitmap();
- sOverrides.isImageType = true;
+ ChooserActivityOverrideData.getInstance().previewThumbnail = createBitmap();
+ ChooserActivityOverrideData.getInstance().isImageType = true;
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
-
- MetricsLogger mockLogger = sOverrides.metricsLogger;
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
+
+ MetricsLogger mockLogger = ChooserActivityOverrideData.getInstance().metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
@@ -938,14 +1131,22 @@ public class ChooserActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_filename")).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_filename"))
+ .check(matches(withText("app.pdf")));
+ onView(withIdFromRuntimeResource("content_preview_file_icon"))
+ .check(matches(isDisplayed()));
}
@@ -962,14 +1163,23 @@ public class ChooserActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf + 2 files")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_filename"))
+ .check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_filename"))
+ .check(matches(withText("app.pdf + 2 files")));
+ onView(withIdFromRuntimeResource("content_preview_file_icon"))
+ .check(matches(isDisplayed()));
}
@Test
@@ -982,17 +1192,25 @@ public class ChooserActivityTest {
Intent sendIntent = createSendUriIntentWithPreview(uris);
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
-
- sOverrides.resolverForceException = true;
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
+
+ ChooserActivityOverrideData.getInstance().resolverForceException = true;
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_filename")).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_filename"))
+ .check(matches(withText("app.pdf")));
+ onView(withIdFromRuntimeResource("content_preview_file_icon"))
+ .check(matches(isDisplayed()));
}
@Test
@@ -1006,9 +1224,15 @@ public class ChooserActivityTest {
Intent sendIntent = createSendUriIntentWithPreview(uris);
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
Cursor cursor = mock(Cursor.class);
when(cursor.getCount()).thenReturn(1);
@@ -1016,13 +1240,15 @@ public class ChooserActivityTest {
when(cursor.moveToFirst()).thenReturn(true);
when(cursor.getColumnIndex(Mockito.anyString())).thenReturn(-1);
- sOverrides.resolverCursor = cursor;
+ ChooserActivityOverrideData.getInstance().resolverCursor = cursor;
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf + 1 file")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_filename")).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("content_preview_filename"))
+ .check(matches(withText("app.pdf + 1 file")));
+ onView(withIdFromRuntimeResource("content_preview_file_icon"))
+ .check(matches(isDisplayed()));
}
@Test
@@ -1032,14 +1258,24 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
- when(sOverrides.resolverListController.getScore(Mockito.isA(DisplayResolveInfo.class)))
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getScore(Mockito.isA(DisplayResolveInfo.class)))
.thenReturn(testBaseScore);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
final DisplayResolveInfo testDri =
@@ -1066,12 +1302,18 @@ public class ChooserActivityTest {
public void testIsAppPredictionServiceAvailable() {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final ChooserActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
if (activity.getPackageManager().getAppPredictionServicePackageName() == null) {
@@ -1079,8 +1321,16 @@ public class ChooserActivityTest {
} else {
assertThat(activity.isAppPredictionServiceAvailable(), is(true));
- sOverrides.resources = Mockito.spy(activity.getResources());
- when(sOverrides.resources.getString(R.string.config_defaultAppPredictionService))
+ ChooserActivityOverrideData.getInstance().resources =
+ Mockito.spy(activity.getResources());
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resources
+ .getString(
+ getRuntimeResourceId(
+ "config_defaultAppPredictionService",
+ "string")))
.thenReturn("ComponentNameThatDoesNotExist");
assertThat(activity.isAppPredictionServiceAvailable(), is(false));
@@ -1091,12 +1341,18 @@ public class ChooserActivityTest {
public void testConvertToChooserTarget_predictionService() {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final ChooserActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
List<ShareShortcutInfo> shortcuts = createShortcuts(activity);
@@ -1127,12 +1383,18 @@ public class ChooserActivityTest {
public void testConvertToChooserTarget_shortcutManager() {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final ChooserActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
List<ShareShortcutInfo> shortcuts = createShortcuts(activity);
@@ -1165,20 +1427,26 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
// Set up resources
- MetricsLogger mockLogger = sOverrides.metricsLogger;
+ MetricsLogger mockLogger = ChooserActivityOverrideData.getInstance().metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
// Create direct share target
List<ChooserTarget> serviceTargets = createDirectShareTargets(1, "");
ResolveInfo ri = ResolverDataProvider.createResolveInfo(3, 0);
// Start activity
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
// Insert the direct share target
Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
@@ -1235,12 +1503,18 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
// Set up resources
- MetricsLogger mockLogger = sOverrides.metricsLogger;
+ MetricsLogger mockLogger = ChooserActivityOverrideData.getInstance().metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
// Create direct share target
List<ChooserTarget> serviceTargets = createDirectShareTargets(1,
@@ -1248,8 +1522,8 @@ public class ChooserActivityTest {
ResolveInfo ri = ResolverDataProvider.createResolveInfo(3, 0);
// Start activity
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
// Insert the direct share target
Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
@@ -1298,24 +1572,36 @@ public class ChooserActivityTest {
@Test @Ignore
public void testShortcutTargetWithApplyAppLimits() throws InterruptedException {
// Set up resources
- sOverrides.resources = Mockito.spy(
+ ChooserActivityOverrideData.getInstance().resources = Mockito.spy(
InstrumentationRegistry.getInstrumentation().getContext().getResources());
- when(sOverrides.resources.getInteger(R.integer.config_maxShortcutTargetsPerApp))
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resources
+ .getInteger(
+ getRuntimeResourceId("config_maxShortcutTargetsPerApp", "integer")))
.thenReturn(1);
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
// Create direct share target
List<ChooserTarget> serviceTargets = createDirectShareTargets(2,
resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
ResolveInfo ri = ResolverDataProvider.createResolveInfo(3, 0);
// Start activity
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final ChooserActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper wrapper = (IChooserWrapper) activity;
// Insert the direct share target
Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
@@ -1325,8 +1611,8 @@ public class ChooserActivityTest {
directShareToShortcutInfos.put(serviceTargets.get(1),
shortcutInfos.get(1).getShortcutInfo());
InstrumentationRegistry.getInstrumentation().runOnMainSync(
- () -> activity.getAdapter().addServiceResults(
- activity.createTestDisplayResolveInfo(sendIntent,
+ () -> wrapper.getAdapter().addServiceResults(
+ wrapper.createTestDisplayResolveInfo(sendIntent,
ri,
"testLabel",
"testInfo",
@@ -1342,13 +1628,13 @@ public class ChooserActivityTest {
Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
assertThat("Chooser should have 3 targets (2 apps, 1 direct)",
- activity.getAdapter().getCount(), is(3));
+ wrapper.getAdapter().getCount(), is(3));
assertThat("Chooser should have exactly one selectable direct target",
- activity.getAdapter().getSelectableServiceTargetCount(), is(1));
+ wrapper.getAdapter().getSelectableServiceTargetCount(), is(1));
assertThat("The resolver info must match the resolver info used to create the target",
- activity.getAdapter().getItem(0).getResolveInfo(), is(ri));
+ wrapper.getAdapter().getItem(0).getResolveInfo(), is(ri));
assertThat("The display label must match",
- activity.getAdapter().getItem(0).getDisplayLabel(), is("testTitle0"));
+ wrapper.getAdapter().getItem(0).getDisplayLabel(), is("testTitle0"));
}
@Test @Ignore
@@ -1358,24 +1644,36 @@ public class ChooserActivityTest {
Boolean.toString(false),
true /* makeDefault*/);
// Set up resources
- sOverrides.resources = Mockito.spy(
+ ChooserActivityOverrideData.getInstance().resources = Mockito.spy(
InstrumentationRegistry.getInstrumentation().getContext().getResources());
- when(sOverrides.resources.getInteger(R.integer.config_maxShortcutTargetsPerApp))
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resources
+ .getInteger(
+ getRuntimeResourceId("config_maxShortcutTargetsPerApp", "integer")))
.thenReturn(1);
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
// Create direct share target
List<ChooserTarget> serviceTargets = createDirectShareTargets(2,
resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
ResolveInfo ri = ResolverDataProvider.createResolveInfo(3, 0);
// Start activity
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final ChooserActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper wrapper = (IChooserWrapper) activity;
// Insert the direct share target
Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
@@ -1385,8 +1683,8 @@ public class ChooserActivityTest {
directShareToShortcutInfos.put(serviceTargets.get(1),
shortcutInfos.get(1).getShortcutInfo());
InstrumentationRegistry.getInstrumentation().runOnMainSync(
- () -> activity.getAdapter().addServiceResults(
- activity.createTestDisplayResolveInfo(sendIntent,
+ () -> wrapper.getAdapter().addServiceResults(
+ wrapper.createTestDisplayResolveInfo(sendIntent,
ri,
"testLabel",
"testInfo",
@@ -1402,15 +1700,15 @@ public class ChooserActivityTest {
Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
assertThat("Chooser should have 4 targets (2 apps, 2 direct)",
- activity.getAdapter().getCount(), is(4));
+ wrapper.getAdapter().getCount(), is(4));
assertThat("Chooser should have exactly two selectable direct target",
- activity.getAdapter().getSelectableServiceTargetCount(), is(2));
+ wrapper.getAdapter().getSelectableServiceTargetCount(), is(2));
assertThat("The resolver info must match the resolver info used to create the target",
- activity.getAdapter().getItem(0).getResolveInfo(), is(ri));
+ wrapper.getAdapter().getItem(0).getResolveInfo(), is(ri));
assertThat("The display label must match",
- activity.getAdapter().getItem(0).getDisplayLabel(), is("testTitle0"));
+ wrapper.getAdapter().getItem(0).getDisplayLabel(), is("testTitle0"));
assertThat("The display label must match",
- activity.getAdapter().getItem(1).getDisplayLabel(), is("testTitle1"));
+ wrapper.getAdapter().getItem(1).getDisplayLabel(), is("testTitle1"));
}
// This test is too long and too slow and should not be taken as an example for future tests.
@@ -1434,19 +1732,30 @@ public class ChooserActivityTest {
.getResources().getConfiguration());
configuration.orientation = orientation;
- sOverrides.resources = Mockito.spy(
+ ChooserActivityOverrideData.getInstance().resources = Mockito.spy(
InstrumentationRegistry.getInstrumentation().getContext().getResources());
- when(sOverrides.resources.getConfiguration()).thenReturn(configuration);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resources
+ .getConfiguration())
+ .thenReturn(configuration);
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(15);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
// Set up resources
- MetricsLogger mockLogger = sOverrides.metricsLogger;
+ MetricsLogger mockLogger = ChooserActivityOverrideData.getInstance().metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
// Create direct share target
List<ChooserTarget> serviceTargets = createDirectShareTargets(1,
@@ -1454,14 +1763,15 @@ public class ChooserActivityTest {
ResolveInfo ri = ResolverDataProvider.createResolveInfo(16, 0);
// Start activity
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper wrapper = (IChooserWrapper) activity;
// Insert the direct share target
Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
directShareToShortcutInfos.put(serviceTargets.get(0), null);
InstrumentationRegistry.getInstrumentation().runOnMainSync(
- () -> activity.getAdapter().addServiceResults(
- activity.createTestDisplayResolveInfo(sendIntent,
+ () -> wrapper.getAdapter().addServiceResults(
+ wrapper.createTestDisplayResolveInfo(sendIntent,
ri,
"testLabel",
"testInfo",
@@ -1479,11 +1789,11 @@ public class ChooserActivityTest {
assertThat(
String.format("Chooser should have %d targets (%d apps, 1 direct, 15 A-Z)",
appTargetsExpected + 16, appTargetsExpected),
- activity.getAdapter().getCount(), is(appTargetsExpected + 16));
+ wrapper.getAdapter().getCount(), is(appTargetsExpected + 16));
assertThat("Chooser should have exactly one selectable direct target",
- activity.getAdapter().getSelectableServiceTargetCount(), is(1));
+ wrapper.getAdapter().getSelectableServiceTargetCount(), is(1));
assertThat("The resolver info must match the resolver info used to create the target",
- activity.getAdapter().getItem(0).getResolveInfo(), is(ri));
+ wrapper.getAdapter().getItem(0).getResolveInfo(), is(ri));
// Click on the direct target
String name = serviceTargets.get(0).getTitle().toString();
@@ -1513,7 +1823,7 @@ public class ChooserActivityTest {
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withId(R.id.tabs)).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("tabs")).check(matches(isDisplayed()));
}
@Test
@@ -1526,7 +1836,7 @@ public class ChooserActivityTest {
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withId(R.id.tabs)).check(matches(not(isDisplayed())));
+ onView(withIdFromRuntimeResource("tabs")).check(matches(not(isDisplayed())));
}
@Test
@@ -1546,12 +1856,12 @@ public class ChooserActivityTest {
sendIntent.setType(TEST_MIME_TYPE);
markWorkProfileUserAvailable();
- final ChooserWrapperActivity activity =
+ final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
assertThat(activity.getPersonalListAdapter().getCount(), is(personalProfileTargets));
assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets));
@@ -1571,10 +1881,10 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
- final ChooserWrapperActivity activity =
+ final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets));
@@ -1594,14 +1904,14 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
// wait for the share sheet to expand
Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
@@ -1625,20 +1935,19 @@ public class ChooserActivityTest {
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets);
- sOverrides.hasCrossProfileIntents = false;
+ ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false;
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
- final ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
- onView(withId(R.id.contentPanel))
+ onView(withIdFromRuntimeResource("contentPanel"))
.perform(swipeUp());
- onView(withText(R.string.resolver_cross_profile_blocked))
+ onView(withTextFromRuntimeResource("resolver_cross_profile_blocked"))
.check(matches(isDisplayed()));
}
@@ -1651,21 +1960,20 @@ public class ChooserActivityTest {
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets);
- sOverrides.isQuietModeEnabled = true;
+ ChooserActivityOverrideData.getInstance().isQuietModeEnabled = true;
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
ResolverActivity.ENABLE_TABBED_VIEW = true;
- final ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withId(R.id.contentPanel))
+ onView(withIdFromRuntimeResource("contentPanel"))
.perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
- onView(withText(R.string.resolver_turn_on_work_apps))
+ onView(withTextFromRuntimeResource("resolver_turn_on_work_apps"))
.check(matches(isDisplayed()));
}
@@ -1682,15 +1990,14 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
- final ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withId(R.id.contentPanel))
+ onView(withIdFromRuntimeResource("contentPanel"))
.perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
- onView(withText(R.string.resolver_no_work_apps_available))
+ onView(withTextFromRuntimeResource("resolver_no_work_apps_available"))
.check(matches(isDisplayed()));
}
@@ -1704,19 +2011,19 @@ public class ChooserActivityTest {
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(0);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- sOverrides.isQuietModeEnabled = true;
- sOverrides.hasCrossProfileIntents = false;
+ ChooserActivityOverrideData.getInstance().isQuietModeEnabled = true;
+ ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false;
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withId(R.id.contentPanel))
+ onView(withIdFromRuntimeResource("contentPanel"))
.perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
- onView(withText(R.string.resolver_cross_profile_blocked))
+ onView(withTextFromRuntimeResource("resolver_cross_profile_blocked"))
.check(matches(isDisplayed()));
}
@@ -1730,18 +2037,18 @@ public class ChooserActivityTest {
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(0);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- sOverrides.isQuietModeEnabled = true;
+ ChooserActivityOverrideData.getInstance().isQuietModeEnabled = true;
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withId(R.id.contentPanel))
+ onView(withIdFromRuntimeResource("contentPanel"))
.perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
- onView(withText(R.string.resolver_no_work_apps_available))
+ onView(withTextFromRuntimeResource("resolver_no_work_apps_available"))
.check(matches(isDisplayed()));
}
@@ -1750,19 +2057,25 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
assertThat(activity.getAdapter().getCount(), is(2));
- onView(withId(R.id.profile_button)).check(doesNotExist());
+ onView(withIdFromRuntimeResource("profile_button")).check(doesNotExist());
ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
@@ -1821,9 +2134,15 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
// Create direct share target
List<ChooserTarget> serviceTargets = createDirectShareTargets(1,
@@ -1831,8 +2150,8 @@ public class ChooserActivityTest {
ResolveInfo ri = ResolverDataProvider.createResolveInfo(3, 0);
// Start activity
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
// Insert the direct share target
Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
@@ -1901,13 +2220,19 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
// Start activity
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
// Thread.sleep shouldn't be a thing in an integration test but it's
// necessary here because of the way the code is structured
@@ -1962,17 +2287,22 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
- final ChooserWrapperActivity activity = mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, null));
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.chooser_copy_button)).check(matches(isDisplayed()));
- onView(withId(R.id.chooser_copy_button)).perform(click());
+ onView(withIdFromRuntimeResource("chooser_copy_button")).check(matches(isDisplayed()));
+ onView(withIdFromRuntimeResource("chooser_copy_button")).perform(click());
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
@@ -2032,12 +2362,12 @@ public class ChooserActivityTest {
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
- final ChooserWrapperActivity activity =
+ final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
- onView(withText(R.string.resolver_personal_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_personal_tab")).perform(click());
waitForIdle();
ChooserActivityLoggerFake logger =
@@ -2108,14 +2438,19 @@ public class ChooserActivityTest {
ResolverActivity.ENABLE_TABBED_VIEW = false;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
@@ -2132,14 +2467,19 @@ public class ChooserActivityTest {
ResolverActivity.ENABLE_TABBED_VIEW = false;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTest(1);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
@@ -2161,11 +2501,11 @@ public class ChooserActivityTest {
createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets);
- sOverrides.hasCrossProfileIntents = false;
+ ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false;
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
@@ -2180,27 +2520,37 @@ public class ChooserActivityTest {
public void testOneInitialIntent_noAutolaunch() {
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTest(1);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent chooserIntent = createChooserIntent(createSendTextIntent(),
new Intent[] {new Intent("action.fake")});
ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartCallback = targetInfo -> {
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
- sOverrides.packageManager = mock(PackageManager.class);
+ ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
ResolveInfo ri = createFakeResolveInfo();
- when(sOverrides.packageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(ri);
+ when(
+ ChooserActivityOverrideData
+ .getInstance().packageManager
+ .resolveActivity(any(Intent.class), anyInt()))
+ .thenReturn(ri);
waitForIdle();
- ChooserWrapperActivity activity = mActivityRule.launchActivity(chooserIntent);
+ IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(chooserIntent);
waitForIdle();
assertNull(chosen[0]);
- assertThat(activity.getPersonalListAdapter().getCallerTargetCount(), is(1));
+ assertThat(activity
+ .getPersonalListAdapter().getCallerTargetCount(), is(1));
}
@Test
@@ -2219,12 +2569,16 @@ public class ChooserActivityTest {
new Intent("action.fake2")
};
Intent chooserIntent = createChooserIntent(createSendTextIntent(), initialIntents);
- sOverrides.packageManager = mock(PackageManager.class);
- when(sOverrides.packageManager.resolveActivity(any(Intent.class), anyInt()))
+ ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .packageManager
+ .resolveActivity(any(Intent.class), anyInt()))
.thenReturn(createFakeResolveInfo());
waitForIdle();
- ChooserWrapperActivity activity = mActivityRule.launchActivity(chooserIntent);
+ IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(chooserIntent);
waitForIdle();
assertThat(activity.getPersonalListAdapter().getCallerTargetCount(), is(2));
@@ -2241,25 +2595,29 @@ public class ChooserActivityTest {
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets);
- sOverrides.hasCrossProfileIntents = false;
+ ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false;
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent[] initialIntents = {
new Intent("action.fake1"),
new Intent("action.fake2")
};
Intent chooserIntent = createChooserIntent(new Intent(), initialIntents);
- sOverrides.packageManager = mock(PackageManager.class);
- when(sOverrides.packageManager.resolveActivity(any(Intent.class), anyInt()))
+ ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .packageManager
+ .resolveActivity(any(Intent.class), anyInt()))
.thenReturn(createFakeResolveInfo());
- final ChooserWrapperActivity activity = mActivityRule.launchActivity(chooserIntent);
+ mActivityRule.launchActivity(chooserIntent);
waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
- onView(withId(R.id.contentPanel))
+ onView(withIdFromRuntimeResource("contentPanel"))
.perform(swipeUp());
- onView(withText(R.string.resolver_cross_profile_blocked))
+ onView(withTextFromRuntimeResource("resolver_cross_profile_blocked"))
.check(matches(isDisplayed()));
}
@@ -2278,18 +2636,22 @@ public class ChooserActivityTest {
new Intent("action.fake2")
};
Intent chooserIntent = createChooserIntent(new Intent(), initialIntents);
- sOverrides.packageManager = mock(PackageManager.class);
- when(sOverrides.packageManager.resolveActivity(any(Intent.class), anyInt()))
+ ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .packageManager
+ .resolveActivity(any(Intent.class), anyInt()))
.thenReturn(createFakeResolveInfo());
mActivityRule.launchActivity(chooserIntent);
waitForIdle();
- onView(withId(R.id.contentPanel))
+ onView(withIdFromRuntimeResource("contentPanel"))
.perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
- onView(withText(R.string.resolver_no_work_apps_available))
+ onView(withTextFromRuntimeResource("resolver_no_work_apps_available"))
.check(matches(isDisplayed()));
}
@@ -2298,20 +2660,26 @@ public class ChooserActivityTest {
// Create 4 ranked app targets.
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTest(4);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
- Mockito.isA(List.class)))
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
// Create caller target which is duplicate with one of app targets
Intent chooserIntent = createChooserIntent(createSendTextIntent(),
new Intent[] {new Intent("action.fake")});
- sOverrides.packageManager = mock(PackageManager.class);
+ ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
ResolveInfo ri = ResolverDataProvider.createResolveInfo(0,
UserHandle.USER_CURRENT);
- when(sOverrides.packageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(ri);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .packageManager
+ .resolveActivity(any(Intent.class), anyInt()))
+ .thenReturn(ri);
waitForIdle();
- ChooserWrapperActivity activity = mActivityRule.launchActivity(chooserIntent);
+ IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(chooserIntent);
waitForIdle();
// Total 4 targets (1 caller target, 3 ranked targets)
@@ -2330,21 +2698,22 @@ public class ChooserActivityTest {
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(3);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- sOverrides.isQuietModeEnabled = true;
+ ChooserActivityOverrideData.getInstance().isQuietModeEnabled = true;
boolean[] isQueryDirectShareCalledOnWorkProfile = new boolean[] { false };
- sOverrides.onQueryDirectShareTargets = chooserListAdapter -> {
- isQueryDirectShareCalledOnWorkProfile[0] =
- (chooserListAdapter.getUserHandle().getIdentifier() == 10);
- return null;
- };
+ ChooserActivityOverrideData.getInstance().onQueryDirectShareTargets =
+ chooserListAdapter -> {
+ isQueryDirectShareCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withId(R.id.contentPanel))
+ onView(withIdFromRuntimeResource("contentPanel"))
.perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
assertFalse("Direct share targets were queried on a paused work profile",
@@ -2361,21 +2730,22 @@ public class ChooserActivityTest {
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(3);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- sOverrides.isWorkProfileUserRunning = false;
+ ChooserActivityOverrideData.getInstance().isWorkProfileUserRunning = false;
boolean[] isQueryDirectShareCalledOnWorkProfile = new boolean[] { false };
- sOverrides.onQueryDirectShareTargets = chooserListAdapter -> {
- isQueryDirectShareCalledOnWorkProfile[0] =
- (chooserListAdapter.getUserHandle().getIdentifier() == 10);
- return null;
- };
+ ChooserActivityOverrideData.getInstance().onQueryDirectShareTargets =
+ chooserListAdapter -> {
+ isQueryDirectShareCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withId(R.id.contentPanel))
+ onView(withIdFromRuntimeResource("contentPanel"))
.perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
assertFalse("Direct share targets were queried on a locked work profile user",
@@ -2394,17 +2764,17 @@ public class ChooserActivityTest {
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
- sOverrides.isWorkProfileUserRunning = false;
+ ChooserActivityOverrideData.getInstance().isWorkProfileUserRunning = false;
- final ChooserWrapperActivity activity =
+ final ChooserActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ final IChooserWrapper wrapper = (IChooserWrapper) activity;
waitForIdle();
- onView(withId(R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withIdFromRuntimeResource("contentPanel")).perform(swipeUp());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
- assertEquals(3, activity.getWorkListAdapter().getCount());
+ assertEquals(3, wrapper.getWorkListAdapter().getCount());
}
@Test
@@ -2417,21 +2787,22 @@ public class ChooserActivityTest {
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(3);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- sOverrides.isWorkProfileUserUnlocked = false;
+ ChooserActivityOverrideData.getInstance().isWorkProfileUserUnlocked = false;
boolean[] isQueryDirectShareCalledOnWorkProfile = new boolean[] { false };
- sOverrides.onQueryDirectShareTargets = chooserListAdapter -> {
- isQueryDirectShareCalledOnWorkProfile[0] =
- (chooserListAdapter.getUserHandle().getIdentifier() == 10);
- return null;
- };
+ ChooserActivityOverrideData.getInstance().onQueryDirectShareTargets =
+ chooserListAdapter -> {
+ isQueryDirectShareCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
- onView(withId(R.id.contentPanel))
+ onView(withIdFromRuntimeResource("contentPanel"))
.perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
assertFalse("Direct share targets were queried on a locked work profile user",
@@ -2450,17 +2821,18 @@ public class ChooserActivityTest {
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
- sOverrides.isWorkProfileUserUnlocked = false;
+ ChooserActivityOverrideData.getInstance().isWorkProfileUserUnlocked = false;
- final ChooserWrapperActivity activity =
+ final ChooserActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ final IChooserWrapper wrapper = (IChooserWrapper) activity;
waitForIdle();
- onView(withId(R.id.contentPanel))
+ onView(withIdFromRuntimeResource("contentPanel"))
.perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
+ onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
waitForIdle();
- assertEquals(3, activity.getWorkListAdapter().getCount());
+ assertEquals(3, wrapper.getWorkListAdapter().getCount());
}
private Intent createChooserIntent(Intent intent, Intent[] initialIntents) {
@@ -2671,23 +3043,62 @@ public class ChooserActivityTest {
}
private void markWorkProfileUserAvailable() {
- sOverrides.workProfileUserHandle = UserHandle.of(10);
+ ChooserActivityOverrideData.getInstance().workProfileUserHandle = UserHandle.of(10);
}
private void setupResolverControllers(
List<ResolvedComponentInfo> personalResolvedComponentInfos,
List<ResolvedComponentInfo> workResolvedComponentInfos) {
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .workResolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(workResolvedComponentInfos));
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .workResolverListController
+ .getResolversForIntentAsUser(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class),
+ eq(UserHandle.SYSTEM)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
}
+
+ private Matcher<View> withIdFromRuntimeResource(String id) {
+ return withId(getRuntimeResourceId(id, "id"));
+ }
+
+ private Matcher<View> withTextFromRuntimeResource(String id) {
+ return withText(getRuntimeResourceId(id, "string"));
+ }
+
+ // ChooserWrapperActivity inherits from the framework ChooserActivity, so if the framework
+ // resources have been updated since the framework was last built/pushed, the inherited behavior
+ // (which is the focus of our testing) will still be implemented in terms of the old resource
+ // IDs; then when we try to assert those IDs in tests (e.g. `onView(withText(R.string.foo))`),
+ // the expected values won't match. The tests can instead call this method (with the same
+ // general semantics as Resources#getIdentifier() e.g. `getRuntimeResourceId("foo", "string")`)
+ // to refer to the resource by that name in the runtime chooser, regardless of whether the
+ // framework code on the device is up-to-date.
+ // TODO: is there a better way to do this? (Other than abandoning inheritance-based DI wrapper?)
+ private int getRuntimeResourceId(String name, String defType) {
+ int id = mActivityRule.getActivity().getResources().getIdentifier(name, defType, "android");
+ assertThat(id, greaterThan(0));
+ return id;
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 6b3d657f9450..d4f08ba5d65d 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -16,7 +16,6 @@
package com.android.internal.app;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
@@ -41,13 +40,15 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.util.List;
-import java.util.function.Function;
-public class ChooserWrapperActivity extends ChooserActivity {
- /*
- * Simple wrapper around chooser activity to be able to initiate it under test
- */
- static final OverrideData sOverrides = new OverrideData();
+/**
+ * Simple wrapper around chooser activity to be able to initiate it under test with overrides
+ * specified in the {@code ChooserActivityOverrideData} singleton. This should be copy-and-pasted
+ * verbatim to test other {@code ChooserActivity} subclasses (updating only the `extends` to match
+ * the concrete activity under test).
+ */
+public class ChooserWrapperActivity extends ChooserActivity implements IChooserWrapper {
+ static final ChooserActivityOverrideData sOverrides = ChooserActivityOverrideData.getInstance();
private UsageStatsManager mUsm;
@Override
@@ -72,16 +73,19 @@ public class ChooserWrapperActivity extends ChooserActivity {
getChooserActivityLogger());
}
- ChooserListAdapter getAdapter() {
+ @Override
+ public ChooserListAdapter getAdapter() {
return mChooserMultiProfilePagerAdapter.getActiveListAdapter();
}
- ChooserListAdapter getPersonalListAdapter() {
+ @Override
+ public ChooserListAdapter getPersonalListAdapter() {
return ((ChooserGridAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(0))
.getListAdapter();
}
- ChooserListAdapter getWorkListAdapter() {
+ @Override
+ public ChooserListAdapter getWorkListAdapter() {
if (mMultiProfilePagerAdapter.getInactiveListAdapter() == null) {
return null;
}
@@ -89,7 +93,10 @@ public class ChooserWrapperActivity extends ChooserActivity {
.getListAdapter();
}
- boolean getIsSelected() { return mIsSuccessfullySelected; }
+ @Override
+ public boolean getIsSelected() {
+ return mIsSuccessfullySelected;
+ }
@Override
protected ComponentName getNearbySharingComponent() {
@@ -103,7 +110,8 @@ public class ChooserWrapperActivity extends ChooserActivity {
return new ChooserWrapperActivity.EmptyTargetInfo();
}
- UsageStatsManager getUsageStatsManager() {
+ @Override
+ public UsageStatsManager getUsageStatsManager() {
if (mUsm == null) {
mUsm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
}
@@ -172,7 +180,7 @@ public class ChooserWrapperActivity extends ChooserActivity {
}
@Override
- protected ChooserActivityLogger getChooserActivityLogger() {
+ public ChooserActivityLogger getChooserActivityLogger() {
return sOverrides.chooserActivityLogger;
}
@@ -197,6 +205,7 @@ public class ChooserWrapperActivity extends ChooserActivity {
return super.isWorkProfile();
}
+ @Override
public DisplayResolveInfo createTestDisplayResolveInfo(Intent originalIntent, ResolveInfo pri,
CharSequence pLabel, CharSequence pInfo, Intent replacementIntent,
@Nullable ResolveInfoPresentationGetter resolveInfoPresentationGetter) {
@@ -209,7 +218,8 @@ public class ChooserWrapperActivity extends ChooserActivity {
return sOverrides.workProfileUserHandle;
}
- protected UserHandle getCurrentUserHandle() {
+ @Override
+ public UserHandle getCurrentUserHandle() {
return mMultiProfilePagerAdapter.getCurrentUserHandle();
}
@@ -248,75 +258,4 @@ public class ChooserWrapperActivity extends ChooserActivity {
}
return sOverrides.isWorkProfileUserUnlocked;
}
-
- /**
- * We cannot directly mock the activity created since instrumentation creates it.
- * <p>
- * Instead, we use static instances of this object to modify behavior.
- */
- static class OverrideData {
- @SuppressWarnings("Since15")
- public Function<PackageManager, PackageManager> createPackageManager;
- public Function<TargetInfo, Boolean> onSafelyStartCallback;
- public Function<ChooserListAdapter, Void> onQueryDirectShareTargets;
- public ResolverListController resolverListController;
- public ResolverListController workResolverListController;
- public Boolean isVoiceInteraction;
- public boolean isImageType;
- public Cursor resolverCursor;
- public boolean resolverForceException;
- public Bitmap previewThumbnail;
- public MetricsLogger metricsLogger;
- public ChooserActivityLogger chooserActivityLogger;
- public int alternateProfileSetting;
- public Resources resources;
- public UserHandle workProfileUserHandle;
- public boolean hasCrossProfileIntents;
- public boolean isQuietModeEnabled;
- public boolean isWorkProfileUserRunning;
- public boolean isWorkProfileUserUnlocked;
- public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
- public PackageManager packageManager;
-
- public void reset() {
- onSafelyStartCallback = null;
- onQueryDirectShareTargets = null;
- isVoiceInteraction = null;
- createPackageManager = null;
- previewThumbnail = null;
- isImageType = false;
- resolverCursor = null;
- resolverForceException = false;
- resolverListController = mock(ResolverListController.class);
- workResolverListController = mock(ResolverListController.class);
- metricsLogger = mock(MetricsLogger.class);
- chooserActivityLogger = new ChooserActivityLoggerFake();
- alternateProfileSetting = 0;
- resources = null;
- workProfileUserHandle = null;
- hasCrossProfileIntents = true;
- isQuietModeEnabled = false;
- isWorkProfileUserRunning = true;
- isWorkProfileUserUnlocked = true;
- packageManager = null;
- multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
- @Override
- public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
- int targetUserId) {
- return hasCrossProfileIntents;
- }
-
- @Override
- public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
- return isQuietModeEnabled;
- }
-
- @Override
- public void requestQuietModeEnabled(boolean enabled,
- UserHandle workProfileUserHandle) {
- isQuietModeEnabled = enabled;
- }
- };
- }
- }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/IChooserWrapper.java b/core/tests/coretests/src/com/android/internal/app/IChooserWrapper.java
new file mode 100644
index 000000000000..05f82529e760
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/IChooserWrapper.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.annotation.Nullable;
+import android.app.usage.UsageStatsManager;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+
+import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
+import com.android.internal.app.chooser.DisplayResolveInfo;
+
+/**
+ * Test-only extended API capabilities that an instrumented ChooserActivity subclass provides in
+ * order to expose the internals for override/inspection. Implementations should apply the overrides
+ * specified by the {@code ChooserActivityOverrideData} singleton.
+ */
+public interface IChooserWrapper {
+ ChooserListAdapter getAdapter();
+ ChooserListAdapter getPersonalListAdapter();
+ ChooserListAdapter getWorkListAdapter();
+ boolean getIsSelected();
+ UsageStatsManager getUsageStatsManager();
+ DisplayResolveInfo createTestDisplayResolveInfo(Intent originalIntent, ResolveInfo pri,
+ CharSequence pLabel, CharSequence pInfo, Intent replacementIntent,
+ @Nullable ResolveInfoPresentationGetter resolveInfoPresentationGetter);
+ UserHandle getCurrentUserHandle();
+ ChooserActivityLogger getChooserActivityLogger();
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 4733f862ebba..69e617aaac19 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -83,7 +83,7 @@ public class BatteryUsageStatsTest {
final Parcel parcel = Parcel.obtain();
parcel.writeParcelable(outBatteryUsageStats, 0);
- assertThat(parcel.dataSize()).isLessThan(5000);
+ assertThat(parcel.dataSize()).isLessThan(5500);
parcel.setDataPosition(0);
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index 48a1da15d574..8d9d79d0c4d9 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -26,6 +26,8 @@ import static org.mockito.Mockito.when;
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
import android.telephony.DataConnectionRealTimeInfo;
@@ -37,12 +39,15 @@ import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.google.common.collect.Range;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
+@SuppressWarnings("GuardedBy")
public class MobileRadioPowerCalculatorTest {
private static final double PRECISION = 0.00001;
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
@@ -55,7 +60,7 @@ public class MobileRadioPowerCalculatorTest {
.setAveragePower(PowerProfile.POWER_RADIO_SCANNING, 720.0)
.setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX, 1440.0)
.setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX,
- new double[] {720.0, 1080.0, 1440.0, 1800.0, 2160.0})
+ new double[]{720.0, 1080.0, 1440.0, 1800.0, 2160.0})
.initMeasuredEnergyStatsLocked();
@Test
@@ -81,7 +86,7 @@ public class MobileRadioPowerCalculatorTest {
// Note established network
stats.noteNetworkInterfaceForTransports("cellular",
- new int[] { NetworkCapabilities.TRANSPORT_CELLULAR });
+ new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
// Note application network activity
NetworkStats networkStats = new NetworkStats(10000, 1)
@@ -89,7 +94,7 @@ public class MobileRadioPowerCalculatorTest {
mStatsRule.setNetworkStats(networkStats);
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
- new int[] {100, 200, 300, 400, 500}, 600);
+ new int[]{100, 200, 300, 400, 500}, 600);
stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000);
mStatsRule.setTime(12_000_000, 12_000_000);
@@ -119,6 +124,90 @@ public class MobileRadioPowerCalculatorTest {
}
@Test
+ public void testTimerBasedModel_byProcessState() {
+ BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ BatteryStatsImpl.Uid uid = stats.getUidStatsLocked(APP_UID);
+
+ mStatsRule.setTime(1000, 1000);
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+ // Scan for a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
+ TelephonyManager.SIM_STATE_READY,
+ 2000, 2000);
+
+ // Found a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_IN_SERVICE, TelephonyManager.SIM_STATE_READY,
+ 5000, 5000);
+
+ // Note cell signal strength
+ SignalStrength signalStrength = mock(SignalStrength.class);
+ when(signalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_MODERATE);
+ stats.notePhoneSignalStrengthLocked(signalStrength, 5000, 5000);
+
+ stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+ 8_000_000_000L, APP_UID, 8000, 8000);
+
+ // Note established network
+ stats.noteNetworkInterfaceForTransports("cellular",
+ new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
+
+ // Note application network activity
+ mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100));
+
+ stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000);
+
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
+
+ mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200));
+
+ stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000);
+
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC()).isAtMost(0);
+ // 12000-8000 = 4000 ms == 4_000_000 us
+ assertThat(uid.getMobileRadioActiveTimeInProcessState(BatteryConsumer.PROCESS_STATE_ANY))
+ .isEqualTo(4_000_000);
+ assertThat(uid.getMobileRadioActiveTimeInProcessState(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND))
+ .isEqualTo(3_000_000);
+ assertThat(uid.getMobileRadioActiveTimeInProcessState(
+ BatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .isEqualTo(1_000_000);
+ assertThat(uid.getMobileRadioActiveTimeInProcessState(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE))
+ .isEqualTo(0);
+
+ MobileRadioPowerCalculator calculator =
+ new MobileRadioPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+ .powerProfileModeledOnly()
+ .includePowerModels()
+ .includeProcessStateData()
+ .build(), calculator);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+
+ final BatteryConsumer.Key foreground = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ final BatteryConsumer.Key background = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+ final BatteryConsumer.Key fgs = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+ assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(1.2);
+ assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(0.4);
+ assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
+ }
+
+ @Test
public void testMeasuredEnergyBasedModel() {
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
@@ -141,7 +230,7 @@ public class MobileRadioPowerCalculatorTest {
// Note established network
stats.noteNetworkInterfaceForTransports("cellular",
- new int[] { NetworkCapabilities.TRANSPORT_CELLULAR });
+ new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
// Note application network activity
NetworkStats networkStats = new NetworkStats(10000, 1)
@@ -149,7 +238,7 @@ public class MobileRadioPowerCalculatorTest {
mStatsRule.setNetworkStats(networkStats);
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
- new int[] {100, 200, 300, 400, 500}, 600);
+ new int[]{100, 200, 300, 400, 500}, 600);
stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000);
mStatsRule.setTime(12_000_000, 12_000_000);
@@ -177,4 +266,87 @@ public class MobileRadioPowerCalculatorTest {
assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
}
+
+ @Test
+ public void testMeasuredEnergyBasedModel_byProcessState() {
+ BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ BatteryStatsImpl.Uid uid = stats.getUidStatsLocked(APP_UID);
+
+ mStatsRule.setTime(1000, 1000);
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+ // Scan for a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
+ TelephonyManager.SIM_STATE_READY,
+ 2000, 2000);
+
+ // Found a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_IN_SERVICE, TelephonyManager.SIM_STATE_READY,
+ 5000, 5000);
+
+ // Note cell signal strength
+ SignalStrength signalStrength = mock(SignalStrength.class);
+ when(signalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_MODERATE);
+ stats.notePhoneSignalStrengthLocked(signalStrength, 5000, 5000);
+
+ stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+ 8_000_000_000L, APP_UID, 8000, 8000);
+
+ // Note established network
+ stats.noteNetworkInterfaceForTransports("cellular",
+ new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
+
+ // Note application network activity
+ mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100));
+
+ stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000);
+
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
+
+ mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200));
+
+ stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000);
+
+ mStatsRule.setTime(20000, 20000);
+
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC())
+ .isIn(Range.open(20_000_000L, 21_000_000L));
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND))
+ .isIn(Range.open(13_000_000L, 14_000_000L));
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC(
+ BatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .isIn(Range.open(7_000_000L, 8_000_000L));
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE))
+ .isEqualTo(0);
+
+ MobileRadioPowerCalculator calculator =
+ new MobileRadioPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+ .includePowerModels()
+ .includeProcessStateData()
+ .build(), calculator);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+
+ final BatteryConsumer.Key foreground = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ final BatteryConsumer.Key background = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+ final BatteryConsumer.Key fgs = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+ assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(3.62064);
+ assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(2.08130);
+ assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index e4c83f175a71..4faf349aad7e 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -233,6 +233,11 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
public Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis) {
return null;
}
+
+ @Override
+ public Future<?> scheduleSyncDueToProcessStateChange(long delayMillis) {
+ return null;
+ }
}
}
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index dc5bc9708f6a..dbb2cf15dd8e 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -465,16 +465,23 @@ public class MeasuredEnergyStatsTest {
supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
+ final int[] supportedMultiStateBuckets = new int[]{POWER_BUCKET_SCREEN_ON};
final MeasuredEnergyStats.Config config =
new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
- new int[0], new String[]{"s"});
+ supportedMultiStateBuckets, new String[]{"s1", "s2"});
final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
+ stats.setState(1, 0);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10, 1000);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5, 2000);
stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
stats.updateCustomBucket(0, 50);
stats.updateCustomBucket(1, 60);
+ assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 0))
+ .isEqualTo(0);
+ assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 1))
+ .isEqualTo(15);
+
MeasuredEnergyStats.resetIfNotNull(stats);
// All charges should be reset to 0
for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
@@ -485,7 +492,13 @@ public class MeasuredEnergyStatsTest {
assertFalse(stats.isStandardBucketSupported(i));
assertEquals(POWER_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketCharge(i));
}
+
}
+ assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 0))
+ .isEqualTo(0);
+ assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 1))
+ .isEqualTo(0);
+
for (int i = 0; i < customBucketNames.length; i++) {
assertEquals(0, stats.getAccumulatedCustomBucketCharge(i));
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 624940bd3823..f17fa3b17fe5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -427,7 +427,6 @@ applications that come with the platform
<permission name="android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS" />
<permission name="android.permission.MANAGE_COMPANION_DEVICES" />
<permission name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING" />
- <permission name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION" />
<permission name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
<permission name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
<!-- Permission required for testing registering pull atom callbacks. -->
@@ -521,6 +520,8 @@ applications that come with the platform
<permission name="android.permission.LOCK_DEVICE" />
<!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
<permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
+ <!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
+ <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
<!-- Permission required for CTS test - CommunalManagerTest -->
<permission name="android.permission.WRITE_COMMUNAL_STATE" />
<permission name="android.permission.READ_COMMUNAL_STATE" />
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 6bfbd8d55ab0..9584994774a9 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1201,6 +1201,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-846931068": {
+ "message": "Update camera compat control state to %s for taskId=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+ },
"-846078709": {
"message": "Configuration doesn't matter in finishing %s",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 1ace32277f0a..9ca8e3b46317 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -16,13 +16,15 @@
package android.graphics;
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
import android.annotation.NonNull;
import libcore.util.NativeAllocationRegistry;
/**
- * Shader that calculates pixel output with a program (fragment shader) running on a GPU.
- * @hide
+ * Shader that calculates per-pixel color via a user defined Android Graphics Shading Language
+ * (AGSL) function.
*/
public class RuntimeShader extends Shader {
@@ -32,7 +34,7 @@ public class RuntimeShader extends Shader {
RuntimeShader.class.getClassLoader(), nativeGetFinalizer());
}
- private boolean mIsOpaque;
+ private boolean mForceOpaque;
/**
* Current native shader builder instance.
@@ -42,57 +44,270 @@ public class RuntimeShader extends Shader {
/**
* Creates a new RuntimeShader.
*
- * @param sksl The text of SKSL program to run on the GPU.
- * @param uniforms Array of parameters passed by the SKSL shader. Array size depends
- * on number of uniforms declared by sksl.
- * @param isOpaque True if all pixels have alpha 1.0f.
+ * @param shader The text of AGSL shader program to run.
*/
- public RuntimeShader(@NonNull String sksl, boolean isOpaque) {
+ public RuntimeShader(@NonNull String shader) {
+ this(shader, false);
+ }
+
+ /**
+ * Creates a new RuntimeShader.
+ *
+ * @param shader The text of AGSL shader program to run.
+ * @param forceOpaque If true then all pixels produced by the AGSL shader program will have an
+ * alpha of 1.0f.
+ */
+ public RuntimeShader(@NonNull String shader, boolean forceOpaque) {
+ // colorspace is required, but the RuntimeShader always produces colors in the destination
+ // buffer's colorspace regardless of the value specified here.
super(ColorSpace.get(ColorSpace.Named.SRGB));
- mIsOpaque = isOpaque;
- mNativeInstanceRuntimeShaderBuilder = nativeCreateBuilder(sksl);
+ if (shader == null) {
+ throw new NullPointerException("RuntimeShader requires a non-null AGSL string");
+ }
+ mForceOpaque = forceOpaque;
+ mNativeInstanceRuntimeShaderBuilder = nativeCreateBuilder(shader);
NoImagePreloadHolder.sRegistry.registerNativeAllocation(
this, mNativeInstanceRuntimeShaderBuilder);
}
+ public boolean isForceOpaque() {
+ return mForceOpaque;
+ }
+
+ /**
+ * Sets the uniform color value corresponding to this shader. If the shader does not have a
+ * uniform with that name or if the uniform is declared with a type other than vec3 or vec4 and
+ * corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the color uniform declared in the AGSL shader program
+ * @param color the provided sRGB color will be transformed into the shader program's output
+ * colorspace and will be available as a vec4 uniform in the program.
+ */
+ public void setColorUniform(@NonNull String uniformName, @ColorInt int color) {
+ setUniform(uniformName, Color.valueOf(color).getComponents(), true);
+ }
+
+ /**
+ * Sets the uniform color value corresponding to this shader. If the shader does not have a
+ * uniform with that name or if the uniform is declared with a type other than vec3 or vec4 and
+ * corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the color uniform declared in the AGSL shader program
+ * @param color the provided sRGB color will be transformed into the shader program's output
+ * colorspace and will be available as a vec4 uniform in the program.
+ */
+ public void setColorUniform(@NonNull String uniformName, @ColorLong long color) {
+ Color exSRGB = Color.valueOf(color).convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
+ setUniform(uniformName, exSRGB.getComponents(), true);
+ }
+
+ /**
+ * Sets the uniform color value corresponding to this shader. If the shader does not have a
+ * uniform with that name or if the uniform is declared with a type other than vec3 or vec4 and
+ * corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the color uniform declared in the AGSL shader program
+ * @param color the provided sRGB color will be transformed into the shader program's output
+ * colorspace and will be available as a vec4 uniform in the program.
+ */
+ public void setColorUniform(@NonNull String uniformName, @NonNull Color color) {
+ if (color == null) {
+ throw new NullPointerException("The color parameter must not be null");
+ }
+ Color exSRGB = color.convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
+ setUniform(uniformName, exSRGB.getComponents(), true);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than a float or float[1]
+ * then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setFloatUniform(@NonNull String uniformName, float value) {
+ setFloatUniform(uniformName, value, 0.0f, 0.0f, 0.0f, 1);
+ }
+
/**
* Sets the uniform value corresponding to this shader. If the shader does not have a uniform
- * with that name or if the uniform is declared with a type other than float then an
+ * with that name or if the uniform is declared with a type other than vec2 or float[2] then an
* IllegalArgumentException is thrown.
*
- * @param uniformName name matching the uniform declared in the SKSL shader
- * @param value
+ * @param uniformName name matching the uniform declared in the AGSL shader program
*/
- public void setUniform(@NonNull String uniformName, float value) {
- setUniform(uniformName, new float[] {value});
+ public void setFloatUniform(@NonNull String uniformName, float value1, float value2) {
+ setFloatUniform(uniformName, value1, value2, 0.0f, 0.0f, 2);
+
}
/**
* Sets the uniform value corresponding to this shader. If the shader does not have a uniform
- * with that name or if the uniform is declared with a type other than float2/vec2 then an
+ * with that name or if the uniform is declared with a type other than vec3 or float[3] then an
* IllegalArgumentException is thrown.
*
- * @param uniformName name matching the uniform declared in the SKSL shader
- * @param value1
- * @param value2
+ * @param uniformName name matching the uniform declared in the AGSL shader program
*/
- public void setUniform(@NonNull String uniformName, float value1, float value2) {
- setUniform(uniformName, new float[] {value1, value2});
+ public void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+ float value3) {
+ setFloatUniform(uniformName, value1, value2, value3, 0.0f, 3);
+
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than vec4 or float[4] then an
+ * IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+ float value3, float value4) {
+ setFloatUniform(uniformName, value1, value2, value3, value4, 4);
}
/**
* Sets the uniform value corresponding to this shader. If the shader does not have a uniform
- * with that name or if the uniform is declared with a type other than a vecN/floatN where N is
- * the size of the values array then an IllegalArgumentException is thrown.
+ * with that name or if the uniform is declared with a type other than a float (for N=1), vecN,
+ * or float[N] where N is the length of the values param then an IllegalArgumentException is
+ * thrown.
*
- * @param uniformName name matching the uniform declared in the SKSL shader
- * @param values
+ * @param uniformName name matching the uniform declared in the AGSL shader program
*/
+ public void setFloatUniform(@NonNull String uniformName, @NonNull float[] values) {
+ setUniform(uniformName, values, false);
+ }
+
+ /**
+ * Old method signature used by some callers within the platform code
+ * @hide
+ * @deprecated use setFloatUniform instead
+ */
+ @Deprecated
public void setUniform(@NonNull String uniformName, float[] values) {
+ setFloatUniform(uniformName, values);
+ }
+
+ /**
+ * Old method signature used by some callers within the platform code
+ * @hide
+ * @deprecated use setFloatUniform instead
+ */
+ @Deprecated
+ public void setUniform(@NonNull String uniformName, float value) {
+ setFloatUniform(uniformName, value);
+ }
+
+ /**
+ * Old method signature used by some callers within the platform code
+ * @hide
+ * @deprecated use setFloatUniform instead
+ */
+ @Deprecated
+ public void setUniform(@NonNull String uniformName, float value1, float value2) {
+ setFloatUniform(uniformName, value1, value2);
+ }
+
+ private void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+ float value3, float value4, int count) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+
+ nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, value1, value2,
+ value3, value4, count);
+ discardNativeInstance();
+ }
+
+ private void setUniform(@NonNull String uniformName, @NonNull float[] values, boolean isColor) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+ if (values == null) {
+ throw new NullPointerException("The uniform values parameter must not be null");
+ }
+
+ nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, values, isColor);
+ discardNativeInstance();
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than an int or int[1]
+ * then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value) {
+ setIntUniform(uniformName, value, 0, 0, 0, 1);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than ivec2 or int[2] then an
+ * IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value1, int value2) {
+ setIntUniform(uniformName, value1, value2, 0, 0, 2);
+
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than ivec3 or int[3] then an
+ * IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3) {
+ setIntUniform(uniformName, value1, value2, value3, 0, 3);
+
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than ivec4 or int[4] then an
+ * IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value1, int value2,
+ int value3, int value4) {
+ setIntUniform(uniformName, value1, value2, value3, value4, 4);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than an int (for N=1), ivecN,
+ * or int[N] where N is the length of the values param then an IllegalArgumentException is
+ * thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setIntUniform(@NonNull String uniformName, @NonNull int[] values) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+ if (values == null) {
+ throw new NullPointerException("The uniform values parameter must not be null");
+ }
nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, values);
discardNativeInstance();
}
+ private void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3,
+ int value4, int count) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+
+ nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, value1, value2,
+ value3, value4, count);
+ discardNativeInstance();
+ }
+
/**
* Sets the uniform shader that is declares as input to this shader. If the shader does not
* have a uniform shader with that name then an IllegalArgumentException is thrown.
@@ -101,6 +316,12 @@ public class RuntimeShader extends Shader {
* @param shader shader passed into the SKSL shader for sampling
*/
public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) {
+ if (shaderName == null) {
+ throw new NullPointerException("The shaderName parameter must not be null");
+ }
+ if (shader == null) {
+ throw new NullPointerException("The shader parameter must not be null");
+ }
nativeUpdateShader(
mNativeInstanceRuntimeShaderBuilder, shaderName, shader.getNativeInstance());
discardNativeInstance();
@@ -109,23 +330,28 @@ public class RuntimeShader extends Shader {
/** @hide */
@Override
protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) {
- return nativeCreateShader(mNativeInstanceRuntimeShaderBuilder, nativeMatrix, mIsOpaque);
+ return nativeCreateShader(mNativeInstanceRuntimeShaderBuilder, nativeMatrix, mForceOpaque);
}
- public long getNativeShaderBuilder() {
+ /** @hide */
+ protected long getNativeShaderBuilder() {
return mNativeInstanceRuntimeShaderBuilder;
}
- public boolean isOpaque() {
- return mIsOpaque;
- }
-
private static native long nativeGetFinalizer();
- private static native long nativeCreateBuilder(String sksl);
+ private static native long nativeCreateBuilder(String agsl);
private static native long nativeCreateShader(
long shaderBuilder, long matrix, boolean isOpaque);
private static native void nativeUpdateUniforms(
- long shaderBuilder, String uniformName, float[] uniforms);
+ long shaderBuilder, String uniformName, float[] uniforms, boolean isColor);
+ private static native void nativeUpdateUniforms(
+ long shaderBuilder, String uniformName, float value1, float value2, float value3,
+ float value4, int count);
+ private static native void nativeUpdateUniforms(
+ long shaderBuilder, String uniformName, int[] uniforms);
+ private static native void nativeUpdateUniforms(
+ long shaderBuilder, String uniformName, int value1, int value2, int value3,
+ int value4, int count);
private static native void nativeUpdateShader(
long shaderBuilder, String shaderName, long shader);
}
diff --git a/keystore/java/android/security/KeyStoreException.java b/keystore/java/android/security/KeyStoreException.java
index 30389a29d342..6db2745840f4 100644
--- a/keystore/java/android/security/KeyStoreException.java
+++ b/keystore/java/android/security/KeyStoreException.java
@@ -16,25 +16,448 @@
package android.security;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.security.keymaster.KeymasterDefs;
+import android.system.keystore2.ResponseCode;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
/**
- * KeyStore/keymaster exception with positive error codes coming from the KeyStore and negative
- * ones from keymaster.
+ * Exception containing information about the failure at the Keystore / KeyMint layer while
+ * generating or using a key.
*
- * @hide
+ * The public error codes indicate the cause of the error and the methods indicate whether
+ * it's a system/key issue and whether re-trying the operation (with the same key or a new key)
+ * is likely to succeed.
*/
-@TestApi
public class KeyStoreException extends Exception {
+ /**
+ * This error code is for mapping errors that the caller will not know about. If the caller is
+ * targeting an API level earlier than the one the error was introduced in, then the error will
+ * be mapped to this one.
+ * In API level 33 no errors map to this error.
+ */
+ public static final int ERROR_OTHER = 1;
+ /**
+ * Indicating the key could not be used because the user needs to authenticate first.
+ * See
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserAuthenticationRequired(boolean)}.
+ */
+ public static final int ERROR_USER_AUTHENTICATION_REQUIRED = 2;
+ /**
+ * Indicating that {@code load()} has not been called on the Keystore instance, or an attempt
+ * has been made to generate an authorization bound key while the user has not set a lock
+ * screen knowledge factor (LSKF). Instruct the user to set an LSKF and retry.
+ */
+ public static final int ERROR_KEYSTORE_UNINITIALIZED = 3;
+ /**
+ * An internal system error - refer to {@link #isTransientFailure()} to determine whether
+ * re-trying the operation is likely to yield different results.
+ */
+ public static final int ERROR_INTERNAL_SYSTEM_ERROR = 4;
+ /**
+ * The caller has requested key parameters or operation which are only available to system
+ * or privileged apps.
+ */
+ public static final int ERROR_PERMISSION_DENIED = 5;
+ /**
+ * The key the operation refers to doesn't exist.
+ */
+ public static final int ERROR_KEY_DOES_NOT_EXIST = 6;
+ /**
+ * The key is corrupted and could not be recovered.
+ */
+ public static final int ERROR_KEY_CORRUPTED = 7;
+ /**
+ * The error related to inclusion of device identifiers in the attestation record.
+ */
+ public static final int ERROR_ID_ATTESTATION_FAILURE = 8;
+ /**
+ * The attestation challenge specified is too large.
+ */
+ public static final int ERROR_ATTESTATION_CHALLENGE_TOO_LARGE = 9;
+ /**
+ * General error in the KeyMint layer.
+ */
+ public static final int ERROR_KEYMINT_FAILURE = 10;
+ /**
+ * Failure in the Keystore layer.
+ */
+ public static final int ERROR_KEYSTORE_FAILURE = 11;
+ /**
+ * The feature the caller is trying to use is not implemented by the underlying
+ * KeyMint implementation.
+ * This could happen when an unsupported algorithm is requested, or when trying to import
+ * a key in a format other than raw or PKCS#8.
+ */
+ public static final int ERROR_UNIMPLEMENTED = 12;
+ /**
+ * The feature the caller is trying to use is not compatible with the parameters used to
+ * generate the key. For example, trying to use a key generated for a different signature
+ * algorithm, or a digest not specified during key creation.
+ * Another case is the attempt to generate a symmetric AES key and requesting key attestation.
+ */
+ public static final int ERROR_INCORRECT_USAGE = 13;
+ /**
+ * The key is not currently valid: Either at has expired or it will be valid for use in the
+ * future.
+ */
+ public static final int ERROR_KEY_NOT_TEMPORALLY_VALID = 14;
+ /**
+ * The crypto object the caller has been using held a reference to a KeyMint operation that
+ * has been evacuated (likely due to other concurrent operations taking place).
+ * The caller should re-create the crypto object and try again.
+ */
+ public static final int ERROR_KEY_OPERATION_EXPIRED = 15;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = {"ERROR_"}, value = {
+ ERROR_OTHER,
+ ERROR_USER_AUTHENTICATION_REQUIRED,
+ ERROR_KEYSTORE_UNINITIALIZED,
+ ERROR_INTERNAL_SYSTEM_ERROR,
+ ERROR_PERMISSION_DENIED,
+ ERROR_KEY_DOES_NOT_EXIST,
+ ERROR_KEY_CORRUPTED,
+ ERROR_ID_ATTESTATION_FAILURE,
+ ERROR_ATTESTATION_CHALLENGE_TOO_LARGE,
+ ERROR_KEYMINT_FAILURE,
+ ERROR_KEYSTORE_FAILURE,
+ ERROR_UNIMPLEMENTED,
+ ERROR_INCORRECT_USAGE,
+ ERROR_KEY_NOT_TEMPORALLY_VALID,
+ ERROR_KEY_OPERATION_EXPIRED
+ })
+ public @interface PublicErrorCode {
+ }
+ // Constants for encoding information about the error encountered:
+ // Whether the error relates to the system state/implementation as a whole, or a specific key.
+ private static final int IS_SYSTEM_ERROR = 1 << 1;
+ // Whether the error is permanent.
+ private static final int IS_TRANSIENT_ERROR = 1 << 2;
+ // Whether the cause of the error is the user not having authenticated recently.
+ private static final int REQUIRES_USER_AUTHENTICATION = 1 << 3;
+
+ // The internal error code. NOT to be returned directly to callers or made part of the
+ // public API.
private final int mErrorCode;
- public KeyStoreException(int errorCode, String message) {
+ /**
+ * @hide
+ */
+ public KeyStoreException(int errorCode, @Nullable String message) {
super(message);
mErrorCode = errorCode;
}
+ /**
+ * Returns the internal error code. Only for use by the platform.
+ *
+ * @hide
+ */
+ @TestApi
public int getErrorCode() {
return mErrorCode;
}
+
+ /**
+ * Returns one of the error codes exported by the class.
+ *
+ * @return a public error code, one of the values in {@link PublicErrorCode}.
+ */
+ @PublicErrorCode
+ public int getNumericErrorCode() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return failureInfo.errorCode;
+ }
+
+ /**
+ * Returns true if the failure is a transient failure - that is, performing the same operation
+ * again at a late time is likely to succeed.
+ *
+ * If {@link #isSystemError()} returns true, the transient nature of the failure relates to the
+ * device, otherwise relates to the key (so a permanent failure with an existing key likely
+ * requires creating another key to repeat the operation with).
+ */
+ public boolean isTransientFailure() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0;
+ }
+
+ /**
+ * Indicates whether the failure is due to the device being locked.
+ *
+ * @return true if the key operation failed because the user has to authenticate
+ * (e.g. by unlocking the device).
+ */
+ public boolean requiresUserAuthentication() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return (failureInfo.indicators & REQUIRES_USER_AUTHENTICATION) != 0;
+ }
+
+ /**
+ * Indicates whether the error related to the Keystore/KeyMint implementation and not
+ * a specific key.
+ *
+ * @return true if the error is related to the system, not the key in use. System
+ * errors indicate a feature isn't working, whereas key-related errors are likely
+ * to succeed with a new key.
+ */
+ public boolean isSystemError() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return (failureInfo.indicators & IS_SYSTEM_ERROR) != 0;
+ }
+
+ @Override
+ public String toString() {
+ String errorCodes = String.format(" (public error code: %d internal Keystore code: %d)",
+ getNumericErrorCode(), mErrorCode);
+ return super.toString() + errorCodes;
+ }
+
+ private static PublicErrorInformation getErrorInformation(int internalErrorCode) {
+ PublicErrorInformation errorInfo = sErrorCodeToFailureInfo.get(internalErrorCode);
+ if (errorInfo != null) {
+ return errorInfo;
+ }
+
+ /**
+ * KeyStore/keymaster exception with positive error codes coming from the KeyStore and
+ * negative ones from keymaster.
+ * This is a safety fall-back: All error codes should be present in the map.
+ */
+ if (internalErrorCode > 0) {
+ return GENERAL_KEYSTORE_ERROR;
+ } else {
+ return GENERAL_KEYMINT_ERROR;
+ }
+ }
+
+ private static final class PublicErrorInformation {
+ public final int indicators;
+ public final int errorCode;
+
+ PublicErrorInformation(int indicators, @PublicErrorCode int errorCode) {
+ this.indicators = indicators;
+ this.errorCode = errorCode;
+ }
+ }
+
+ private static final PublicErrorInformation GENERAL_KEYMINT_ERROR =
+ new PublicErrorInformation(0, ERROR_KEYMINT_FAILURE);
+
+ private static final PublicErrorInformation GENERAL_KEYSTORE_ERROR =
+ new PublicErrorInformation(0, ERROR_KEYSTORE_FAILURE);
+
+ private static final PublicErrorInformation KEYMINT_UNIMPLEMENTED_ERROR =
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_UNIMPLEMENTED);
+
+ private static final PublicErrorInformation KEYMINT_RETRYABLE_ERROR =
+ new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
+ ERROR_KEYMINT_FAILURE);
+
+ private static final PublicErrorInformation KEYMINT_INCORRECT_USAGE_ERROR =
+ new PublicErrorInformation(0, ERROR_INCORRECT_USAGE);
+
+ private static final PublicErrorInformation KEYMINT_TEMPORAL_VALIDITY_ERROR =
+ new PublicErrorInformation(0, ERROR_KEY_NOT_TEMPORALLY_VALID);
+
+
+ private static final Map<Integer, PublicErrorInformation> sErrorCodeToFailureInfo =
+ new HashMap();
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public static boolean hasFailureInfoForError(int internalErrorCode) {
+ return sErrorCodeToFailureInfo.containsKey(internalErrorCode);
+ }
+
+ static {
+ // KeyMint error codes
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OK, GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ROOT_OF_TRUST_ALREADY_SET,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_PURPOSE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_PURPOSE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_ALGORITHM,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_ALGORITHM,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_SIZE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_BLOCK_MODE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_BLOCK_MODE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MAC_LENGTH,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_PADDING_MODE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_PADDING_MODE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_DIGEST,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_DIGEST,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_EXPIRATION_TIME,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_USER_ID,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_FORMAT,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_KEY_FORMAT,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_VERIFICATION_ALGORITHM,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_EXPORT_OPTIONS_INVALID,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_DELEGATION_NOT_ALLOWED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID,
+ KEYMINT_TEMPORAL_VALIDITY_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_EXPIRED,
+ KEYMINT_TEMPORAL_VALIDITY_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ new PublicErrorInformation(REQUIRES_USER_AUTHENTICATION,
+ ERROR_USER_AUTHENTICATION_REQUIRED));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OUTPUT_PARAMETER_NULL,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE,
+ new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
+ ERROR_KEY_OPERATION_EXPIRED));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INSUFFICIENT_BUFFER_SPACE,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_VERIFICATION_FAILED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_TOO_MANY_OPERATIONS,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNEXPECTED_NULL_POINTER,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_KEY_BLOB,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_NOT_ENCRYPTED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_DECRYPTION_FAILED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_NOT_SIGNED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_VERIFICATION_FAILED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_ARGUMENT,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_TAG,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_TAG,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MEMORY_ALLOCATION_FAILED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORT_PARAMETER_MISMATCH,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_ACCESS_DENIED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OPERATION_CANCELLED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CONCURRENT_ACCESS_CONFLICT,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_BUSY,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_COMMUNICATION_FAILED,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_EC_FIELD,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NONCE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_NONCE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CALLER_NONCE_PROHIBITED,
+ GENERAL_KEYMINT_ERROR);
+ // Error related to MAX_USES_PER_BOOT, restricting the number of uses per boot.
+ // It is not re-tryable.
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_MAX_OPS_EXCEEDED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_MIN_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KDF,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_EC_CURVE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ATTESTATION_CHALLENGE_MISSING,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEYMINT_NOT_CONFIGURED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_KEYMINT_FAILURE));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ATTESTATION_APPLICATION_ID_MISSING,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CANNOT_ATTEST_IDS,
+ new PublicErrorInformation(IS_SYSTEM_ERROR,
+ ERROR_ID_ATTESTATION_FAILURE));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_DEVICE_LOCKED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR | REQUIRES_USER_AUTHENTICATION,
+ ERROR_USER_AUTHENTICATION_REQUIRED));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_STORAGE_KEY_UNSUPPORTED,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_MGF_DIGEST,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MGF_DIGEST,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NOT_BEFORE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NOT_AFTER,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ // This should not be exposed to apps as it's handled by Keystore.
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_HARDWARE_NOT_YET_AVAILABLE,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNIMPLEMENTED,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
+ new PublicErrorInformation(IS_SYSTEM_ERROR,
+ ERROR_KEYMINT_FAILURE));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_VERSION_MISMATCH, GENERAL_KEYMINT_ERROR);
+
+ // Keystore error codes
+ sErrorCodeToFailureInfo.put(ResponseCode.LOCKED,
+ new PublicErrorInformation(REQUIRES_USER_AUTHENTICATION,
+ ERROR_USER_AUTHENTICATION_REQUIRED));
+ sErrorCodeToFailureInfo.put(ResponseCode.UNINITIALIZED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_KEYSTORE_UNINITIALIZED));
+ sErrorCodeToFailureInfo.put(ResponseCode.SYSTEM_ERROR,
+ new PublicErrorInformation(IS_SYSTEM_ERROR,
+ ERROR_INTERNAL_SYSTEM_ERROR));
+ sErrorCodeToFailureInfo.put(ResponseCode.PERMISSION_DENIED,
+ new PublicErrorInformation(0, ERROR_PERMISSION_DENIED));
+ sErrorCodeToFailureInfo.put(ResponseCode.KEY_NOT_FOUND,
+ new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST));
+ sErrorCodeToFailureInfo.put(ResponseCode.VALUE_CORRUPTED,
+ new PublicErrorInformation(0, ERROR_KEY_CORRUPTED));
+ sErrorCodeToFailureInfo.put(ResponseCode.KEY_PERMANENTLY_INVALIDATED,
+ new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST));
+ }
}
diff --git a/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml b/libs/WindowManager/Shell/res/color/compat_background_ripple.xml
index 329e5b9b31a0..329e5b9b31a0 100644
--- a/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml
+++ b/libs/WindowManager/Shell/res/color/compat_background_ripple.xml
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml
new file mode 100644
index 000000000000..1c8cb914af81
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml
@@ -0,0 +1,33 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="43dp"
+ android:viewportWidth="48"
+ android:viewportHeight="43">
+ <group>
+ <clip-path
+ android:pathData="M48,43l-48,-0l-0,-43l48,-0z"/>
+ <path
+ android:pathData="M24,43C37.2548,43 48,32.2548 48,19L48,0L0,-0L0,19C0,32.2548 10.7452,43 24,43Z"
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"/>
+ <path
+ android:pathData="M31,12.41L29.59,11L24,16.59L18.41,11L17,12.41L22.59,18L17,23.59L18.41,25L24,19.41L29.59,25L31,23.59L25.41,18L31,12.41Z"
+ android:fillColor="@color/compat_controls_text"/>
+ </group>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml
new file mode 100644
index 000000000000..c81013966c35
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/camera_compat_dismiss_button"/>
+</ripple> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml
new file mode 100644
index 000000000000..c796b5967f98
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml
@@ -0,0 +1,32 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="43dp"
+ android:viewportWidth="48"
+ android:viewportHeight="43">
+ <path
+ android:pathData="M24,0C10.7452,0 0,10.7452 0,24V43H48V24C48,10.7452 37.2548,0 24,0Z"
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"/>
+ <path
+ android:pathData="M32,17H28.83L27,15H21L19.17,17H16C14.9,17 14,17.9 14,19V31C14,32.1 14.9,33 16,33H32C33.1,33 34,32.1 34,31V19C34,17.9 33.1,17 32,17ZM32,31H16V19H32V31Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M24.6618,22C23.0436,22 21.578,22.6187 20.4483,23.625L18.25,21.375V27H23.7458L21.5353,24.7375C22.3841,24.0125 23.4649,23.5625 24.6618,23.5625C26.8235,23.5625 28.6616,25.0062 29.3028,27L30.75,26.5125C29.9012,23.8938 27.5013,22 24.6618,22Z"
+ android:fillColor="@color/compat_controls_text"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml
new file mode 100644
index 000000000000..3e9fe6dc3b99
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/camera_compat_treatment_applied_button"/>
+</ripple>
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml
new file mode 100644
index 000000000000..af505d1cb73c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml
@@ -0,0 +1,53 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="43dp"
+ android:viewportWidth="48"
+ android:viewportHeight="43">
+ <path
+ android:pathData="M24,0C10.7452,0 0,10.7452 0,24V43H48V24C48,10.7452 37.2548,0 24,0Z"
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"/>
+ <path
+ android:pathData="M32,17H28.83L27,15H21L19.17,17H16C14.9,17 14,17.9 14,19V31C14,32.1 14.9,33 16,33H32C33.1,33 34,32.1 34,31V19C34,17.9 33.1,17 32,17ZM32,31H16V19H32V31Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,29L18,25.5L19.5,25.5L19.5,29L18,29Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,29L30,25.5L28.5,25.5L28.5,29L30,29Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,21L30,24.5L28.5,24.5L28.5,21L30,21Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,21L18,24.5L19.5,24.5L19.5,21L18,21Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,27.5L21.5,27.5L21.5,29L18,29L18,27.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,27.5L26.5,27.5L26.5,29L30,29L30,27.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,22.5L26.5,22.5L26.5,21L30,21L30,22.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,22.5L21.5,22.5L21.5,21L18,21L18,22.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml
new file mode 100644
index 000000000000..c0f1c89b0cbb
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/camera_compat_treatment_suggested_button"/>
+</ripple>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index ab74e43472c3..e6ae28207970 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -21,7 +21,9 @@
android:viewportHeight="48">
<path
android:fillColor="@color/compat_controls_background"
- android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" />
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"
+ android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0"/>
<group
android:translateX="12"
android:translateY="12">
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
index 95decff24ac4..6551edf6d0e6 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
@@ -15,6 +15,6 @@
~ limitations under the License.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/size_compat_background_ripple">
+ android:color="@color/compat_background_ripple">
<item android:drawable="@drawable/size_compat_restart_button"/>
</ripple> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
index c04e258ea784..4ac972c6cfa7 100644
--- a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
@@ -16,7 +16,7 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:clipToPadding="false"
@@ -26,7 +26,7 @@
<TextView
android:id="@+id/compat_mode_hint_text"
- android:layout_width="188dp"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="4sp"
android:background="@drawable/compat_hint_bubble"
diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
index 6f946b25eaa5..c99f3fe89563 100644
--- a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -21,24 +21,51 @@
android:orientation="vertical"
android:gravity="bottom|end">
- <include android:id="@+id/size_compat_hint"
- layout="@layout/compat_mode_hint"/>
+ <include android:id="@+id/camera_compat_hint"
+ android:visibility="gone"
+ android:layout_width="@dimen/camera_compat_hint_width"
+ android:layout_height="wrap_content"
+ layout="@layout/compat_mode_hint"/>
- <FrameLayout
- android:layout_width="@dimen/size_compat_button_width"
- android:layout_height="@dimen/size_compat_button_height"
+ <LinearLayout
+ android:id="@+id/camera_compat_control"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:clipToPadding="false"
- android:paddingBottom="16dp">
+ android:layout_marginEnd="16dp"
+ android:layout_marginBottom="16dp"
+ android:orientation="vertical">
+
+ <ImageButton
+ android:id="@+id/camera_compat_treatment_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"/>
<ImageButton
- android:id="@+id/size_compat_restart_button"
+ android:id="@+id/camera_compat_dismiss_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:src="@drawable/size_compat_restart_button_ripple"
+ android:src="@drawable/camera_compat_dismiss_ripple"
android:background="@android:color/transparent"
- android:contentDescription="@string/restart_button_description"/>
+ android:contentDescription="@string/camera_compat_dismiss_button_description"/>
+
+ </LinearLayout>
+
+ <include android:id="@+id/size_compat_hint"
+ android:visibility="gone"
+ android:layout_width="@dimen/size_compat_hint_width"
+ android:layout_height="wrap_content"
+ layout="@layout/compat_mode_hint"/>
- </FrameLayout>
+ <ImageButton
+ android:id="@+id/size_compat_restart_button"
+ android:visibility="gone"
+ android:layout_width="@dimen/size_compat_button_width"
+ android:layout_height="@dimen/size_compat_button_height"
+ android:src="@drawable/size_compat_restart_button_ripple"
+ android:background="@android:color/transparent"
+ android:contentDescription="@string/restart_button_description"/>
</com.android.wm.shell.compatui.CompatUILayout>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 18e91f41a698..d338e3bd74f9 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -216,6 +216,12 @@
- compat_hint_corner_radius - compat_hint_point_width /2). -->
<dimen name="compat_hint_padding_end">7dp</dimen>
+ <!-- The width of the size compat hint. -->
+ <dimen name="size_compat_hint_width">188dp</dimen>
+
+ <!-- The width of the camera compat hint. -->
+ <dimen name="camera_compat_hint_width">143dp</dimen>
+
<!-- The width of the brand image on staring surface. -->
<dimen name="starting_surface_brand_image_width">200dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index c88fc16e218e..ab0013a2b0b4 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -158,4 +158,17 @@
<!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
<string name="restart_button_description">Tap to restart this app and go full screen.</string>
+
+ <!-- Description of the camera compat button for applying stretched issues treatment in the hint for
+ compatibility control. [CHAR LIMIT=NONE] -->
+ <string name="camera_compat_treatment_suggested_button_description">Camera issues?\nTap to refit</string>
+
+ <!-- Description of the camera compat button for reverting stretched issues treatment in the hint for
+ compatibility control. [CHAR LIMIT=NONE] -->
+ <string name="camera_compat_treatment_applied_button_description">Didn\u2019t fix it?\nTap to revert</string>
+
+ <!-- Accessibillity description of the camera dismiss button for stretched issues in the hint for
+ compatibility control. [CHAR LIMIT=NONE] -->
+ <string name="camera_compat_dismiss_button_description">No camera issues? Tap to dismiss.</string>
+
</resources>
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 8b3a35688f11..91ea436f81b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -458,7 +458,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
newListener.onTaskInfoChanged(taskInfo);
}
notifyLocusVisibilityIfNeeded(taskInfo);
- if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) {
+ if (updated || !taskInfo.equalsForCompatUi(data.getTaskInfo())) {
// Notify the compat UI if the listener or task info changed.
notifyCompatUI(taskInfo, newListener);
}
@@ -607,6 +607,19 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token);
}
+ @Override
+ public void onCameraControlStateUpdated(
+ int taskId, @TaskInfo.CameraCompatControlState int state) {
+ final TaskAppearedInfo info;
+ synchronized (mLock) {
+ info = mTasks.get(taskId);
+ }
+ if (info == null) {
+ return;
+ }
+ updateCameraCompatControlState(info.getTaskInfo().token, state);
+ }
+
private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info,
int event) {
ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo;
@@ -633,14 +646,11 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
// The task is vanished or doesn't support compat UI, notify to remove compat UI
// on this Task if there is any.
if (taskListener == null || !taskListener.supportCompatUI()
- || !taskInfo.topActivityInSizeCompat || !taskInfo.isVisible) {
- mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
- null /* taskConfig */, null /* taskListener */);
+ || !taskInfo.hasCompatUI() || !taskInfo.isVisible) {
+ mCompatUI.onCompatInfoChanged(taskInfo, null /* taskListener */);
return;
}
-
- mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
- taskInfo.configuration, taskListener);
+ mCompatUI.onCompatInfoChanged(taskInfo, taskListener);
}
private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index b8ac87fa5d4c..a9e0c489150b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -180,8 +180,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
/** Applies new configuration, returns {@code false} if there's no effect to the layout. */
public boolean updateConfiguration(Configuration configuration) {
- boolean affectsLayout = false;
-
// Update the split bounds when necessary. Besides root bounds changed, split bounds need to
// be updated when the rotation changed to cover the case that users rotated the screen 180
// degrees.
@@ -214,6 +212,24 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
return true;
}
+ /** Rotate the layout to specific rotation and assume its new bounds. */
+ public void rotateTo(int newRotation) {
+ final int rotationDelta = (newRotation - mRotation + 4) % 4;
+ final boolean changeOrient = (rotationDelta % 2) != 0;
+
+ mRotation = newRotation;
+ Rect tmpRect = new Rect(mRootBounds);
+ if (changeOrient) {
+ tmpRect.set(mRootBounds.top, mRootBounds.left, mRootBounds.bottom, mRootBounds.right);
+ }
+
+ final Configuration config = new Configuration();
+ config.setTo(mContext.getResources().getConfiguration());
+ config.orientation = mOrientation;
+ config.windowConfiguration.setBounds(tmpRect);
+ updateConfiguration(config);
+ }
+
private void initDividerPosition(Rect oldBounds) {
final float snapRatio = (float) mDividePosition
/ (float) (isLandscape(oldBounds) ? oldBounds.width() : oldBounds.height());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index e0b23873a980..8f4cfb0a49a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -17,6 +17,8 @@
package com.android.wm.shell.compatui;
import android.annotation.Nullable;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
@@ -53,12 +55,14 @@ import java.util.function.Predicate;
public class CompatUIController implements OnDisplaysChangedListener,
DisplayImeController.ImePositionProcessor {
- /** Callback for size compat UI interaction. */
+ /** Callback for compat UI interaction. */
public interface CompatUICallback {
/** Called when the size compat restart button appears. */
void onSizeCompatRestartButtonAppeared(int taskId);
/** Called when the size compat restart button is clicked. */
void onSizeCompatRestartButtonClicked(int taskId);
+ /** Called when the camera compat control state is updated. */
+ void onCameraControlStateUpdated(int taskId, @CameraCompatControlState int state);
}
private static final String TAG = "CompatUIController";
@@ -86,10 +90,12 @@ public class CompatUIController implements OnDisplaysChangedListener,
private CompatUICallback mCallback;
- /** Only show once automatically in the process life. */
- private boolean mHasShownHint;
- /** Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
- * be shown. */
+ // Only show once automatically in the process life.
+ private boolean mHasShownSizeCompatHint;
+ private boolean mHasShownCameraCompatHint;
+
+ // Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
+ // be shown.
private boolean mKeyguardOccluded;
public CompatUIController(Context context,
@@ -122,23 +128,20 @@ public class CompatUIController implements OnDisplaysChangedListener,
* Called when the Task info changed. Creates and updates the compat UI if there is an
* activity in size compat, or removes the UI if there is no size compat activity.
*
- * @param displayId display the task and activity are in.
- * @param taskId task the activity is in.
- * @param taskConfig task config to place the compat UI with.
+ * @param taskInfo {@link TaskInfo} task the activity is in.
* @param taskListener listener to handle the Task Surface placement.
*/
- public void onCompatInfoChanged(int displayId, int taskId,
- @Nullable Configuration taskConfig,
+ public void onCompatInfoChanged(TaskInfo taskInfo,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
- if (taskConfig == null || taskListener == null) {
+ if (taskInfo.configuration == null || taskListener == null) {
// Null token means the current foreground activity is not in compatibility mode.
- removeLayout(taskId);
- } else if (mActiveLayouts.contains(taskId)) {
+ removeLayout(taskInfo.taskId);
+ } else if (mActiveLayouts.contains(taskInfo.taskId)) {
// UI already exists, update the UI layout.
- updateLayout(taskId, taskConfig, taskListener);
+ updateLayout(taskInfo, taskListener);
} else {
// Create a new compat UI.
- createLayout(displayId, taskId, taskConfig, taskListener);
+ createLayout(taskInfo, taskListener);
}
}
@@ -215,38 +218,45 @@ public class CompatUIController implements OnDisplaysChangedListener,
return mDisplaysWithIme.contains(displayId);
}
- private void createLayout(int displayId, int taskId, Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener) {
- final Context context = getOrCreateDisplayContext(displayId);
+ private void createLayout(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+ final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
- Log.e(TAG, "Cannot get context for display " + displayId);
+ Log.e(TAG, "Cannot get context for display " + taskInfo.displayId);
return;
}
final CompatUIWindowManager compatUIWindowManager =
- createLayout(context, displayId, taskId, taskConfig, taskListener);
- mActiveLayouts.put(taskId, compatUIWindowManager);
- compatUIWindowManager.createLayout(showOnDisplay(displayId));
+ createLayout(context, taskInfo, taskListener);
+ mActiveLayouts.put(taskInfo.taskId, compatUIWindowManager);
+ compatUIWindowManager.createLayout(showOnDisplay(taskInfo.displayId),
+ taskInfo.topActivityInSizeCompat, taskInfo.cameraCompatControlState);
}
@VisibleForTesting
- CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
- Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
+ CompatUIWindowManager createLayout(Context context, TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener) {
final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context,
- taskConfig, mSyncQueue, mCallback, taskId, taskListener,
- mDisplayController.getDisplayLayout(displayId), mHasShownHint);
- // Only show hint for the first time.
- mHasShownHint = true;
+ taskInfo.configuration, mSyncQueue, mCallback, taskInfo.taskId, taskListener,
+ mDisplayController.getDisplayLayout(taskInfo.displayId), mHasShownSizeCompatHint,
+ mHasShownCameraCompatHint);
+ // Only show hints for the first time.
+ if (taskInfo.topActivityInSizeCompat) {
+ mHasShownSizeCompatHint = true;
+ }
+ if (taskInfo.hasCameraCompatControl()) {
+ mHasShownCameraCompatHint = true;
+ }
return compatUIWindowManager;
}
- private void updateLayout(int taskId, Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener) {
- final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
+ private void updateLayout(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+ final CompatUIWindowManager layout = mActiveLayouts.get(taskInfo.taskId);
if (layout == null) {
return;
}
- layout.updateCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
+ layout.updateCompatInfo(taskInfo.configuration, taskListener,
+ showOnDisplay(layout.getDisplayId()), taskInfo.topActivityInSizeCompat,
+ taskInfo.cameraCompatControlState);
}
private void removeLayout(int taskId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
index ea4f20968438..29b2baa221e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
@@ -16,6 +16,9 @@
package com.android.wm.shell.compatui;
+import android.annotation.IdRes;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
@@ -53,21 +56,59 @@ public class CompatUILayout extends LinearLayout {
mWindowManager = windowManager;
}
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- // Need to relayout after changes like hiding / showing a hint since they affect size.
- // Doing this directly in setSizeCompatHintVisibility can result in flaky animation.
- mWindowManager.relayout();
+ void updateCameraTreatmentButton(@CameraCompatControlState int newState) {
+ int buttonBkgId = newState == TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
+ ? R.drawable.camera_compat_treatment_suggested_ripple
+ : R.drawable.camera_compat_treatment_applied_ripple;
+ int hintStringId = newState == TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
+ ? R.string.camera_compat_treatment_suggested_button_description
+ : R.string.camera_compat_treatment_applied_button_description;
+ final ImageButton button = findViewById(R.id.camera_compat_treatment_button);
+ button.setImageResource(buttonBkgId);
+ button.setContentDescription(getResources().getString(hintStringId));
+ final LinearLayout hint = findViewById(R.id.camera_compat_hint);
+ ((TextView) hint.findViewById(R.id.compat_mode_hint_text)).setText(hintStringId);
}
void setSizeCompatHintVisibility(boolean show) {
- final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint);
+ setViewVisibility(R.id.size_compat_hint, show);
+ }
+
+ void setCameraCompatHintVisibility(boolean show) {
+ setViewVisibility(R.id.camera_compat_hint, show);
+ }
+
+ void setRestartButtonVisibility(boolean show) {
+ setViewVisibility(R.id.size_compat_restart_button, show);
+ // Hint should never be visible without button.
+ if (!show) {
+ setSizeCompatHintVisibility(/* show= */ false);
+ }
+ }
+
+ void setCameraControlVisibility(boolean show) {
+ setViewVisibility(R.id.camera_compat_control, show);
+ // Hint should never be visible without button.
+ if (!show) {
+ setCameraCompatHintVisibility(/* show= */ false);
+ }
+ }
+
+ private void setViewVisibility(@IdRes int resId, boolean show) {
+ final View view = findViewById(resId);
int visibility = show ? View.VISIBLE : View.GONE;
- if (sizeCompatHint.getVisibility() == visibility) {
+ if (view.getVisibility() == visibility) {
return;
}
- sizeCompatHint.setVisibility(visibility);
+ view.setVisibility(visibility);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ // Need to relayout after changes like hiding / showing a hint since they affect size.
+ // Doing this directly in setSizeCompatHintVisibility can result in flaky animation.
+ mWindowManager.relayout();
}
@Override
@@ -85,5 +126,26 @@ public class CompatUILayout extends LinearLayout {
((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text))
.setText(R.string.restart_button_description);
sizeCompatHint.setOnClickListener(view -> setSizeCompatHintVisibility(/* show= */ false));
+
+ final ImageButton cameraTreatmentButton =
+ findViewById(R.id.camera_compat_treatment_button);
+ cameraTreatmentButton.setOnClickListener(
+ view -> mWindowManager.onCameraTreatmentButtonClicked());
+ cameraTreatmentButton.setOnLongClickListener(view -> {
+ mWindowManager.onCameraButtonLongClicked();
+ return true;
+ });
+
+ final ImageButton cameraDismissButton = findViewById(R.id.camera_compat_dismiss_button);
+ cameraDismissButton.setOnClickListener(
+ view -> mWindowManager.onCameraDismissButtonClicked());
+ cameraDismissButton.setOnLongClickListener(view -> {
+ mWindowManager.onCameraButtonLongClicked();
+ return true;
+ });
+
+ final LinearLayout cameraCompatHint = findViewById(R.id.camera_compat_hint);
+ cameraCompatHint.setOnClickListener(
+ view -> setCameraCompatHintVisibility(/* show= */ false));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 997ad04e3b57..44526b00bf0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -16,6 +16,10 @@
package com.android.wm.shell.compatui;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
@@ -23,6 +27,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERL
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.annotation.Nullable;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
@@ -63,8 +68,17 @@ class CompatUIWindowManager extends WindowlessWindowManager {
private ShellTaskOrganizer.TaskListener mTaskListener;
private DisplayLayout mDisplayLayout;
+ // Remember the last reported states in case visibility changes due to keyguard or
+ // IME updates.
@VisibleForTesting
- boolean mShouldShowHint;
+ boolean mHasSizeCompat;
+ @CameraCompatControlState
+ private int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
+
+ @VisibleForTesting
+ boolean mShouldShowSizeCompatHint;
+ @VisibleForTesting
+ boolean mShouldShowCameraCompatHint;
@Nullable
@VisibleForTesting
@@ -78,7 +92,7 @@ class CompatUIWindowManager extends WindowlessWindowManager {
CompatUIWindowManager(Context context, Configuration taskConfig,
SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback,
int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
- boolean hasShownHint) {
+ boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) {
super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
mContext = context;
mSyncQueue = syncQueue;
@@ -88,7 +102,8 @@ class CompatUIWindowManager extends WindowlessWindowManager {
mTaskId = taskId;
mTaskListener = taskListener;
mDisplayLayout = displayLayout;
- mShouldShowHint = !hasShownHint;
+ mShouldShowSizeCompatHint = !hasShownSizeCompatHint;
+ mShouldShowCameraCompatHint = !hasShownCameraCompatHint;
mStableBounds = new Rect();
mDisplayLayout.getStableBounds(mStableBounds);
}
@@ -113,7 +128,10 @@ class CompatUIWindowManager extends WindowlessWindowManager {
}
/** Creates the layout for compat controls. */
- void createLayout(boolean show) {
+ void createLayout(boolean show, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
+ mHasSizeCompat = hasSizeCompat;
+ mCameraCompatControlState = cameraCompatControlState;
if (!show || mCompatUILayout != null) {
// Wait until compat controls should be visible.
return;
@@ -122,16 +140,27 @@ class CompatUIWindowManager extends WindowlessWindowManager {
initCompatUi();
updateSurfacePosition();
- mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
+ if (hasSizeCompat) {
+ mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
+ }
+ }
+
+ private void createLayout(boolean show) {
+ createLayout(show, mHasSizeCompat, mCameraCompatControlState);
}
/** Called when compat info changed. */
void updateCompatInfo(Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener, boolean show) {
+ ShellTaskOrganizer.TaskListener taskListener, boolean show, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
final Configuration prevTaskConfig = mTaskConfig;
final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
mTaskConfig = taskConfig;
mTaskListener = taskListener;
+ final boolean prevHasSizeCompat = mHasSizeCompat;
+ final int prevCameraCompatControlState = mCameraCompatControlState;
+ mHasSizeCompat = hasSizeCompat;
+ mCameraCompatControlState = cameraCompatControlState;
// Update configuration.
mContext = mContext.createConfigurationContext(taskConfig);
@@ -144,6 +173,11 @@ class CompatUIWindowManager extends WindowlessWindowManager {
return;
}
+ if (prevHasSizeCompat != mHasSizeCompat
+ || prevCameraCompatControlState != mCameraCompatControlState) {
+ updateVisibilityOfViews();
+ }
+
if (!taskConfig.windowConfiguration.getBounds()
.equals(prevTaskConfig.windowConfiguration.getBounds())) {
// Reposition the UI surfaces.
@@ -155,6 +189,7 @@ class CompatUIWindowManager extends WindowlessWindowManager {
mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection());
updateSurfacePosition();
}
+
}
/** Called when the visibility of the UI should change. */
@@ -195,6 +230,34 @@ class CompatUIWindowManager extends WindowlessWindowManager {
mCallback.onSizeCompatRestartButtonClicked(mTaskId);
}
+ /** Called when the camera treatment button is clicked. */
+ void onCameraTreatmentButtonClicked() {
+ if (!shouldShowCameraControl()) {
+ Log.w(TAG, "Camera compat shouldn't receive clicks in the hidden state.");
+ return;
+ }
+ // When a camera control is shown, only two states are allowed: "treament applied" and
+ // "treatment suggested". Clicks on the conrol's treatment button toggle between these
+ // two states.
+ mCameraCompatControlState =
+ mCameraCompatControlState == CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
+ ? CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
+ : CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ mCallback.onCameraControlStateUpdated(mTaskId, mCameraCompatControlState);
+ mCompatUILayout.updateCameraTreatmentButton(mCameraCompatControlState);
+ }
+
+ /** Called when the camera dismiss button is clicked. */
+ void onCameraDismissButtonClicked() {
+ if (!shouldShowCameraControl()) {
+ Log.w(TAG, "Camera compat shouldn't receive clicks in the hidden state.");
+ return;
+ }
+ mCameraCompatControlState = CAMERA_COMPAT_CONTROL_DISMISSED;
+ mCallback.onCameraControlStateUpdated(mTaskId, CAMERA_COMPAT_CONTROL_DISMISSED);
+ mCompatUILayout.setCameraControlVisibility(/* show= */ false);
+ }
+
/** Called when the restart button is long clicked. */
void onRestartButtonLongClicked() {
if (mCompatUILayout == null) {
@@ -203,6 +266,14 @@ class CompatUIWindowManager extends WindowlessWindowManager {
mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
}
+ /** Called when either dismiss or treatment camera buttons is long clicked. */
+ void onCameraButtonLongClicked() {
+ if (mCompatUILayout == null) {
+ return;
+ }
+ mCompatUILayout.setCameraCompatHintVisibility(/* show= */ true);
+ }
+
int getDisplayId() {
return mDisplayId;
}
@@ -213,6 +284,8 @@ class CompatUIWindowManager extends WindowlessWindowManager {
/** Releases the surface control and tears down the view hierarchy. */
void release() {
+ // Hiding before releasing to avoid flickering when transitioning to the Home screen.
+ mCompatUILayout.setVisibility(View.GONE);
mCompatUILayout = null;
if (mViewHost != null) {
@@ -283,12 +356,35 @@ class CompatUIWindowManager extends WindowlessWindowManager {
mCompatUILayout = inflateCompatUILayout();
mCompatUILayout.inject(this);
- mCompatUILayout.setSizeCompatHintVisibility(mShouldShowHint);
+ updateVisibilityOfViews();
mViewHost.setView(mCompatUILayout, getWindowLayoutParams());
+ }
+
+ private void updateVisibilityOfViews() {
+ // Size Compat mode restart button.
+ mCompatUILayout.setRestartButtonVisibility(mHasSizeCompat);
+ if (mHasSizeCompat && mShouldShowSizeCompatHint) {
+ mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
+ // Only show by default for the first time.
+ mShouldShowSizeCompatHint = false;
+ }
+
+ // Camera control for stretched issues.
+ mCompatUILayout.setCameraControlVisibility(shouldShowCameraControl());
+ if (shouldShowCameraControl() && mShouldShowCameraCompatHint) {
+ mCompatUILayout.setCameraCompatHintVisibility(/* show= */ true);
+ // Only show by default for the first time.
+ mShouldShowCameraCompatHint = false;
+ }
+ if (shouldShowCameraControl()) {
+ mCompatUILayout.updateCameraTreatmentButton(mCameraCompatControlState);
+ }
+ }
- // Only show by default for the first time.
- mShouldShowHint = false;
+ private boolean shouldShowCameraControl() {
+ return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
+ && mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
}
@VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index d681a7780295..d70857a4b221 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -161,13 +161,14 @@ public class WMShellModule {
SyncTransactionQueue syncQueue, Context context,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
@ShellMainThread ShellExecutor mainExecutor,
+ DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
return new SplitScreenController(shellTaskOrganizer, syncQueue, context,
- rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
+ rootTaskDisplayAreaOrganizer, mainExecutor, displayController, displayImeController,
displayInsetsController, transitions, transactionPool, iconProvider,
recentTasks, stageTaskUnfoldControllerProvider);
}
@@ -307,9 +308,11 @@ public class WMShellModule {
Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
- PhonePipMenuController pipMenuController) {
+ PhonePipMenuController pipMenuController,
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper) {
return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
- pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
+ pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer,
+ pipSurfaceTransactionHelper);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index b31e6e0750ce..0c443ce51116 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -62,6 +62,7 @@ public class PipTransition extends PipTransitionController {
private final PipTransitionState mPipTransitionState;
private final int mEnterExitAnimationDuration;
+ private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
private Rect mExitDestinationBounds = new Rect();
@@ -74,12 +75,14 @@ public class PipTransition extends PipTransitionController {
PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController,
Transitions transitions,
- @NonNull ShellTaskOrganizer shellTaskOrganizer) {
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper) {
super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
pipAnimationController, transitions, shellTaskOrganizer);
mPipTransitionState = pipTransitionState;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
+ mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
}
@Override
@@ -286,7 +289,10 @@ public class PipTransition extends PipTransitionController {
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
PipAnimationController.PipTransitionAnimator animator;
- finishTransaction.setPosition(leash, destinationBounds.left, destinationBounds.top);
+ // Set corner radius for entering pip.
+ mSurfaceTransactionHelper
+ .crop(finishTransaction, leash, destinationBounds)
+ .round(finishTransaction, leash, true /* applyCornerRadius */);
if (taskInfo.pictureInPictureParams != null
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()
&& mPipTransitionState.getInSwipePipToHomeTransition()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index a006f308d694..3de59b48bcf0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -25,6 +25,7 @@ import android.app.ActivityTaskManager;
import android.app.TaskInfo;
import android.content.Context;
import android.os.RemoteException;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -316,6 +317,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
(controller) -> out[0] = controller.getRecentTasks(maxNum, flags, userId)
.toArray(new GroupedRecentTaskInfo[0]),
true /* blocking */);
+ Slog.d("b/206648922", "getRecentTasks(" + maxNum + "): " + out[0]);
return out[0];
}
}
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 afd51171bdd7..39ad5364f113 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
@@ -58,6 +58,7 @@ import com.android.internal.logging.InstanceId;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
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.DisplayInsetsController;
import com.android.wm.shell.common.RemoteCallable;
@@ -124,6 +125,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
private final ShellExecutor mMainExecutor;
private final SplitScreenImpl mImpl = new SplitScreenImpl();
+ private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
private final DisplayInsetsController mDisplayInsetsController;
private final Transitions mTransitions;
@@ -138,7 +140,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, Context context,
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
- ShellExecutor mainExecutor, DisplayImeController displayImeController,
+ ShellExecutor mainExecutor, DisplayController displayController,
+ DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
@@ -148,6 +151,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mContext = context;
mRootTDAOrganizer = rootTDAOrganizer;
mMainExecutor = mainExecutor;
+ mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
mTransitions = transitions;
@@ -176,7 +180,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
if (mStageCoordinator == null) {
// TODO: Multi-display
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
- mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
+ mRootTDAOrganizer, mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
mIconProvider, mRecentTasksOptional, mUnfoldControllerProvider);
}
@@ -370,7 +374,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps) {
- if (!isSplitScreenVisible()) return null;
+ if (apps.length < 2) return null;
final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
.setContainerLayer()
.setName("RecentsAnimationSplitTasks")
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 86e7b0e4cb7f..54d8ece26c1a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -23,6 +23,10 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
+import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
+import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
@@ -40,7 +44,9 @@ import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.OneShotRemoteHandler;
import com.android.wm.shell.transition.Transitions;
@@ -57,7 +63,7 @@ class SplitScreenTransitions {
private final Transitions mTransitions;
private final Runnable mOnFinish;
- IBinder mPendingDismiss = null;
+ DismissTransition mPendingDismiss = null;
IBinder mPendingEnter = null;
private IBinder mAnimatingTransition = null;
@@ -127,12 +133,6 @@ class SplitScreenTransitions {
}
// TODO(shell-transitions): screenshot here
final Rect startBounds = new Rect(change.getStartAbsBounds());
- if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
- // Dismissing split via snap which means the still-visible task has been
- // dragged to its end position at animation start so reflect that here.
- startBounds.offsetTo(change.getEndAbsBounds().left,
- change.getEndAbsBounds().top);
- }
final Rect endBounds = new Rect(change.getEndAbsBounds());
startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
@@ -144,8 +144,9 @@ class SplitScreenTransitions {
if (transition == mPendingEnter && (mainRoot.equals(change.getContainer())
|| sideRoot.equals(change.getContainer()))) {
- t.setWindowCrop(leash, change.getStartAbsBounds().width(),
- change.getStartAbsBounds().height());
+ t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top);
+ t.setWindowCrop(leash, change.getEndAbsBounds().width(),
+ change.getEndAbsBounds().height());
}
boolean isOpening = isOpeningType(info.getType());
if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
@@ -184,12 +185,20 @@ class SplitScreenTransitions {
return transition;
}
- /** Starts a transition for dismissing split after dragging the divider to a screen edge */
- IBinder startSnapToDismiss(@NonNull WindowContainerTransaction wct,
- @NonNull Transitions.TransitionHandler handler) {
- final IBinder transition = mTransitions.startTransition(
- TRANSIT_SPLIT_DISMISS_SNAP, wct, handler);
- mPendingDismiss = transition;
+ /** Starts a transition to dismiss split. */
+ IBinder startDismissTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
+ Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
+ @SplitScreenController.ExitReason int reason) {
+ final int type = reason == EXIT_REASON_DRAG_DIVIDER
+ ? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS;
+ if (transition == null) {
+ transition = mTransitions.startTransition(type, wct, handler);
+ }
+ mPendingDismiss = new DismissTransition(transition, reason, dismissTop);
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ + " deduced Dismiss due to %s. toTop=%s",
+ exitReasonToString(reason), stageTypeToString(dismissTop));
return transition;
}
@@ -206,7 +215,7 @@ class SplitScreenTransitions {
if (mAnimatingTransition == mPendingEnter) {
mPendingEnter = null;
}
- if (mAnimatingTransition == mPendingDismiss) {
+ if (mPendingDismiss != null && mPendingDismiss.mTransition == mAnimatingTransition) {
mPendingDismiss = null;
}
mAnimatingTransition = null;
@@ -295,4 +304,20 @@ class SplitScreenTransitions {
mAnimations.add(va);
mTransitions.getAnimExecutor().execute(va::start);
}
+
+ /** Bundled information of dismiss transition. */
+ static class DismissTransition {
+ IBinder mTransition;
+
+ int mReason;
+
+ @SplitScreen.StageType
+ int mDismissTop;
+
+ DismissTransition(IBinder transition, int reason, int dismissTop) {
+ this.mTransition = transition;
+ this.mReason = reason;
+ this.mDismissTop = dismissTop;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 5e8f29f7cf18..28f838b9fe5d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -18,9 +18,12 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
@@ -32,7 +35,6 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
-import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
@@ -40,6 +42,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -82,6 +85,7 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
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.DisplayInsetsController;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -117,13 +121,11 @@ import javax.inject.Provider;
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
*/
class StageCoordinator implements SplitLayout.SplitLayoutHandler,
- RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {
+ RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener,
+ DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler {
private static final String TAG = StageCoordinator.class.getSimpleName();
- /** internal value for mDismissTop that represents no dismiss */
- private static final int NO_DISMISS = -2;
-
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final MainStage mMainStage;
@@ -144,8 +146,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private DisplayAreaInfo mDisplayAreaInfo;
private final Context mContext;
private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
+ private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
private final DisplayInsetsController mDisplayInsetsController;
+ private final TransactionPool mTransactionPool;
private final SplitScreenTransitions mSplitTransitions;
private final SplitscreenEventLogger mLogger;
private final Optional<RecentTasksController> mRecentTasks;
@@ -156,9 +160,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private boolean mKeyguardOccluded;
private boolean mDeviceSleep;
- @StageType
- private int mDismissTop = NO_DISMISS;
-
/** The target stage to dismiss to when unlock after folded. */
@StageType
private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
@@ -168,10 +169,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (!isSplitScreenVisible()) {
// Update divider state after animation so that it is still around and positioned
// properly for the animation itself.
- setDividerVisibility(false);
+ mSplitLayout.release();
mSplitLayout.resetDividerPosition();
}
- mDismissTop = NO_DISMISS;
};
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
@@ -189,6 +189,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
+ DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, SplitscreenEventLogger logger,
@@ -223,8 +224,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSurfaceSession,
iconProvider,
mSideUnfoldController);
+ mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
+ mTransactionPool = transactionPool;
mRootTDAOrganizer.registerListener(displayId, this);
final DeviceStateManager deviceStateManager =
mContext.getSystemService(DeviceStateManager.class);
@@ -232,13 +235,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
mOnTransitionAnimationComplete);
+ mDisplayController.addDisplayWindowListener(this);
transitions.addHandler(this);
}
@VisibleForTesting
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
- MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController,
+ MainStage mainStage, SideStage sideStage, DisplayController displayController,
+ DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger,
@@ -251,8 +256,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mTaskOrganizer = taskOrganizer;
mMainStage = mainStage;
mSideStage = sideStage;
+ mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
+ mTransactionPool = transactionPool;
mRootTDAOrganizer.registerListener(displayId, this);
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
@@ -261,6 +268,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mLogger = logger;
mRecentTasks = recentTasks;
+ mDisplayController.addDisplayWindowListener(this);
transitions.addHandler(this);
}
@@ -361,6 +369,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
float splitRatio, RemoteAnimationAdapter adapter) {
+ // Init divider first to make divider leash for remote animation target.
+ mSplitLayout.init();
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Need to add another wrapper here in shell so that we can inject the divider bar
// and also manage the process elevation via setRunningRemote
@@ -377,6 +387,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
augmentedNonApps[i] = nonApps[i];
}
augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
+
+ IRemoteAnimationFinishedCallback wrapCallback =
+ new IRemoteAnimationFinishedCallback.Stub() {
+ @Override
+ public void onAnimationFinished() throws RemoteException {
+ mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
+ finishedCallback.onAnimationFinished();
+ }
+ };
try {
try {
ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
@@ -385,8 +404,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
Slog.e(TAG, "Unable to boost animation thread. This should only happen"
+ " during unit tests");
}
- adapter.getRunner().onAnimationStart(transit, apps, wallpapers, nonApps,
- finishedCallback);
+ adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
+ augmentedNonApps, wrapCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Error starting remote animation", e);
}
@@ -394,6 +413,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onAnimationCancelled() {
+ mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
try {
adapter.getRunner().onAnimationCancelled();
} catch (RemoteException e) {
@@ -461,8 +481,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
}
} else {
- // Exit split-screen and launch fullscreen since stage wasn't specified.
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ Slog.w(TAG,
+ "No stage type nor split position specified to resolve start stage");
}
break;
}
@@ -636,8 +656,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
.setWindowCrop(mSideStage.mRootLeash, null));
// Hide divider and reset its position.
- setDividerVisibility(false);
mSplitLayout.resetDividerPosition();
+ mSplitLayout.release();
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
// Log the exit
@@ -817,29 +837,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- private void setDividerVisibility(boolean visible) {
- if (mDividerVisible == visible) return;
- mDividerVisible = visible;
- if (visible) {
- mSplitLayout.init();
- updateUnfoldBounds();
- } else {
- mSplitLayout.release();
- }
- sendSplitVisibilityChanged();
- }
-
private void onStageVisibilityChanged(StageListenerImpl stageListener) {
final boolean sideStageVisible = mSideStageListener.mVisible;
final boolean mainStageVisible = mMainStageListener.mVisible;
final boolean bothStageVisible = sideStageVisible && mainStageVisible;
final boolean bothStageInvisible = !sideStageVisible && !mainStageVisible;
final boolean sameVisibility = sideStageVisible == mainStageVisible;
- // Only add or remove divider when both visible or both invisible to avoid sometimes we only
- // got one stage visibility changed for a moment and it will cause flicker.
- if (sameVisibility) {
- setDividerVisibility(bothStageVisible);
- }
if (bothStageInvisible) {
if (mExitSplitScreenOnHide
@@ -860,23 +863,30 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (sameVisibility) {
t.setVisibility(mSideStage.mRootLeash, bothStageVisible)
.setVisibility(mMainStage.mRootLeash, bothStageVisible);
- applyDividerVisibility(t);
+ setDividerVisibility(bothStageVisible, t);
}
});
}
+ private void setDividerVisibility(boolean visible, @Nullable SurfaceControl.Transaction t) {
+ mDividerVisible = visible;
+ sendSplitVisibilityChanged();
+ if (t != null) {
+ applyDividerVisibility(t);
+ } else {
+ mSyncQueue.runInSync(transaction -> applyDividerVisibility(transaction));
+ }
+ }
+
private void applyDividerVisibility(SurfaceControl.Transaction t) {
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
- if (dividerLeash == null) {
- return;
- }
+ if (dividerLeash == null) return;
if (mDividerVisible) {
- t.show(dividerLeash)
- .setLayer(dividerLeash, SPLIT_DIVIDER_LAYER)
- .setPosition(dividerLeash,
- mSplitLayout.getDividerBounds().left,
- mSplitLayout.getDividerBounds().top);
+ t.show(dividerLeash);
+ t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER);
+ t.setPosition(dividerLeash,
+ mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top);
} else {
t.hide(dividerLeash);
}
@@ -895,11 +905,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
} else if (isSideStage) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ mSplitLayout.init();
// Make sure the main stage is active.
mMainStage.activate(getMainStageBounds(), wct, true /* reparent */);
mSideStage.moveToTop(getSideStageBounds(), wct);
mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t));
+ mSyncQueue.runInSync(t -> {
+ updateSurfaceBounds(mSplitLayout, t);
+ setDividerVisibility(true, t);
+ });
}
if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
mShouldUpdateRecents = true;
@@ -914,23 +928,22 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- @VisibleForTesting
- IBinder onSnappedToDismissTransition(boolean mainStageToTop) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- prepareExitSplitScreen(mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE, wct);
- return mSplitTransitions.startSnapToDismiss(wct, this);
- }
-
@Override
public void onSnappedToDismiss(boolean bottomOrRight) {
final boolean mainStageToTop =
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
- if (ENABLE_SHELL_TRANSITIONS) {
- onSnappedToDismissTransition(mainStageToTop);
+
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
return;
}
- exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
+
+ final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(dismissTop, wct);
+ mSplitTransitions.startDismissTransition(
+ null /* transition */, wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
}
@Override
@@ -1057,10 +1070,39 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mSplitLayout != null
&& mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)
&& mMainStage.isActive()) {
+ // TODO(b/204925795): With Shell transition, We are handle roation case for apply split
+ // bounds at onRotateDisplay. But still need to handle unfold case.
+ if (ENABLE_SHELL_TRANSITIONS) {
+ updateUnfoldBounds();
+ return;
+ }
onLayoutSizeChanged(mSplitLayout);
}
}
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mDisplayController.addDisplayChangingController(this::onRotateDisplay);
+ }
+
+ private void onRotateDisplay(int displayId, int fromRotation, int toRotation,
+ WindowContainerTransaction wct) {
+ if (!mMainStage.isActive()) return;
+ // Only do this when shell transition
+ if (!ENABLE_SHELL_TRANSITIONS) return;
+
+ final SurfaceControl.Transaction t = mTransactionPool.acquire();
+ setDividerVisibility(false, t);
+ mSplitLayout.rotateTo(toRotation);
+ updateWindowBounds(mSplitLayout, wct);
+ updateUnfoldBounds();
+ t.apply();
+ mTransactionPool.release(t);
+ }
+
private void onFoldedStateChanged(boolean folded) {
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
if (!folded) return;
@@ -1109,14 +1151,25 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Nullable TransitionRequestInfo request) {
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask == null) {
- // still want to monitor everything while in split-screen, so return non-null.
+ // Still want to monitor everything while in split-screen, so return non-null.
return isSplitScreenVisible() ? new WindowContainerTransaction() : null;
+ } else if (triggerTask.displayId != mDisplayId) {
+ // Skip handling task on the other display.
+ return null;
}
WindowContainerTransaction out = null;
final @WindowManager.TransitionType int type = request.getType();
+ final boolean isOpening = isOpeningType(type);
+ final boolean inFullscreen = triggerTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+
+ if (isOpening && inFullscreen) {
+ // One task is opening into fullscreen mode, remove the corresponding split record.
+ mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
+ }
+
if (isSplitScreenVisible()) {
- // try to handle everything while in split-screen, so return a WCT even if it's empty.
+ // Try to handle everything while in split-screen, so return a WCT even if it's empty.
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
+ " sideChildren=%d", triggerTask.taskId, transitTypeToString(type),
@@ -1124,35 +1177,35 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
out = new WindowContainerTransaction();
final StageTaskListener stage = getStageOfTask(triggerTask);
if (stage != null) {
- // dismiss split if the last task in one of the stages is going away
+ // Dismiss split if the last task in one of the stages is going away
if (isClosingType(type) && stage.getChildCount() == 1) {
// The top should be the opposite side that is closing:
- mDismissTop = getStageType(stage) == STAGE_TYPE_MAIN
- ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
+ int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE
+ : STAGE_TYPE_MAIN;
+ prepareExitSplitScreen(dismissTop, out);
+ mSplitTransitions.startDismissTransition(transition, out, this, dismissTop,
+ EXIT_REASON_APP_FINISHED);
}
- } else {
- if (triggerTask.getActivityType() == ACTIVITY_TYPE_HOME && isOpeningType(type)) {
- // Going home so dismiss both.
- mDismissTop = STAGE_TYPE_UNDEFINED;
+ } else if (isOpening && inFullscreen) {
+ final int activityType = triggerTask.getActivityType();
+ if (activityType == ACTIVITY_TYPE_ASSISTANT) {
+ // We don't want assistant panel to dismiss split screen, so do nothing.
+ } else {
+ // Going home or occluded by the other fullscreen task, so dismiss both.
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
+ final int exitReason = activityType == ACTIVITY_TYPE_HOME
+ ? EXIT_REASON_RETURN_HOME : EXIT_REASON_UNKNOWN;
+ mSplitTransitions.startDismissTransition(transition, out, this,
+ STAGE_TYPE_UNDEFINED, exitReason);
}
}
- if (mDismissTop != NO_DISMISS) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
- + " deduced Dismiss from request. toTop=%s",
- stageTypeToString(mDismissTop));
- prepareExitSplitScreen(mDismissTop, out);
- mSplitTransitions.mPendingDismiss = transition;
- }
} else {
- // Not in split mode, so look for an open into a split stage to active split screen.
- if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)) {
- if (getStageOfTask(triggerTask) != null) {
- // One task is appearing in split, prepare to enter split screen.
- out = new WindowContainerTransaction();
- mSplitTransitions.mPendingEnter = transition;
- mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */);
- mSideStage.moveToTop(getSideStageBounds(), out);
- }
+ if (isOpening && getStageOfTask(triggerTask) != null) {
+ // One task is appearing into split, prepare to enter split screen.
+ out = new WindowContainerTransaction();
+ mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */);
+ mSideStage.moveToTop(getSideStageBounds(), out);
+ mSplitTransitions.mPendingEnter = transition;
}
}
return out;
@@ -1164,8 +1217,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (transition != mSplitTransitions.mPendingDismiss
- && transition != mSplitTransitions.mPendingEnter) {
+ if (transition != mSplitTransitions.mPendingEnter && (
+ mSplitTransitions.mPendingDismiss == null
+ || mSplitTransitions.mPendingDismiss.mTransition != transition)) {
// Not entering or exiting, so just do some house-keeping and validation.
// If we're not in split-mode, just abort so something else can handle it.
@@ -1187,6 +1241,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
Log.w(TAG, "Expected onTaskVanished on " + stage + " to have been called"
+ " with " + taskInfo.taskId + " before startAnimation().");
}
+ } else if (info.getType() == TRANSIT_CHANGE
+ && change.getStartRotation() != change.getEndRotation()) {
+ // Show the divider after transition finished.
+ setDividerVisibility(true, finishTransaction);
}
}
if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
@@ -1208,8 +1266,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
boolean shouldAnimate = true;
if (mSplitTransitions.mPendingEnter == transition) {
shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
- } else if (mSplitTransitions.mPendingDismiss == transition) {
- shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction);
+ } else if (mSplitTransitions.mPendingDismiss != null
+ && mSplitTransitions.mPendingDismiss.mTransition == transition) {
+ shouldAnimate = startPendingDismissAnimation(
+ mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
}
if (!shouldAnimate) return false;
@@ -1242,7 +1302,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
// Update local states (before animating).
- setDividerVisibility(true);
+ mSplitLayout.init();
+ setDividerVisibility(true, t);
setSplitsVisible(true);
addDividerBarToTransition(info, t, true /* show */);
@@ -1263,11 +1324,24 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
+ " to have been called with " + sideChild.getTaskInfo().taskId
+ " before startAnimation().");
}
+
+ mShouldUpdateRecents = true;
+ updateRecentTasksSplitPair();
+
+ if (!mLogger.hasStartedSession()) {
+ mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+ getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLandscape());
+ }
+
return true;
}
- private boolean startPendingDismissAnimation(@NonNull IBinder transition,
- @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
+ private boolean startPendingDismissAnimation(
+ @NonNull SplitScreenTransitions.DismissTransition dismissTransition,
+ @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction finishT) {
// Make some noise if things aren't totally expected. These states shouldn't effect
// transitions locally, but remotes (like Launcher) may get confused if they were
// depending on listener callbacks. This can happen because task-organizer callbacks
@@ -1295,6 +1369,21 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
+ "] before startAnimation().");
}
+ mRecentTasks.ifPresent(recentTasks -> {
+ // Notify recents if we are exiting in a way that breaks the pair, and disable further
+ // updates to splits in the recents until we enter split again
+ if (shouldBreakPairedTaskInRecents(dismissTransition.mReason) && mShouldUpdateRecents) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo != null
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ recentTasks.removeSplitPair(taskInfo.taskId);
+ }
+ }
+ }
+ });
+ mShouldUpdateRecents = false;
+
// Update local states.
setSplitsVisible(false);
// Wait until after animation to update divider
@@ -1305,21 +1394,28 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
t.setWindowCrop(mSideStage.mRootLeash, null);
}
- if (mDismissTop == STAGE_TYPE_UNDEFINED) {
- // Going home (dismissing both splits)
-
+ if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
+ logExit(dismissTransition.mReason);
// TODO: Have a proper remote for this. Until then, though, reset state and use the
// normal animation stuff (which falls back to the normal launcher remote).
- t.hide(mSplitLayout.getDividerLeash());
- setDividerVisibility(false);
+ setDividerVisibility(false, t);
+ mSplitLayout.release();
mSplitTransitions.mPendingDismiss = null;
return false;
+ } else {
+ logExitToStage(dismissTransition.mReason,
+ dismissTransition.mDismissTop == STAGE_TYPE_MAIN);
}
addDividerBarToTransition(info, t, false /* show */);
// We're dismissing split by moving the other one to fullscreen.
// Since we don't have any animations for this yet, just use the internal example
// animations.
+
+ // Hide divider and dim layer on transition finished.
+ setDividerVisibility(false, finishT);
+ finishT.hide(mMainStage.mDimLayer);
+ finishT.hide(mSideStage.mDimLayer);
return true;
}
@@ -1457,7 +1553,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onNoLongerSupportMultiWindow() {
if (mMainStage.isActive()) {
- StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
+ EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
+ }
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ mSplitTransitions.startDismissTransition(null /* transition */, wct,
+ StageCoordinator.this, STAGE_TYPE_UNDEFINED,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
index af9a5aa501e8..018365420177 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
@@ -127,12 +127,6 @@ class SplitScreenTransitions {
}
// TODO(shell-transitions): screenshot here
final Rect startBounds = new Rect(change.getStartAbsBounds());
- if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
- // Dismissing split via snap which means the still-visible task has been
- // dragged to its end position at animation start so reflect that here.
- startBounds.offsetTo(change.getEndAbsBounds().left,
- change.getEndAbsBounds().top);
- }
final Rect endBounds = new Rect(change.getEndAbsBounds());
startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index a1c086415063..324b8b53e433 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -358,8 +358,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
// No default animation for this, so just update bounds/position.
startTransaction.setPosition(change.getLeash(),
- change.getEndAbsBounds().left - change.getEndRelOffset().x,
- change.getEndAbsBounds().top - change.getEndRelOffset().y);
+ change.getEndAbsBounds().left - info.getRootOffset().x,
+ change.getEndAbsBounds().top - info.getRootOffset().y);
if (isTask) {
// Skip non-tasks since those usually have null bounds.
startTransaction.setWindowCrop(change.getLeash(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index a7a178872905..b8cbfd9680fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -89,6 +89,9 @@ public class Transitions implements RemoteCallable<Transitions> {
/** Transition type for entering split by opening an app into side-stage. */
public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;
+ /** Transition type for dismissing split-screen. */
+ public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 6;
+
private final WindowOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 16fc0489fbc8..59bbdfee1366 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -33,7 +33,6 @@ import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Assume.assumeFalse
-import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -84,12 +83,6 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
}
}
- @Before
- fun onBefore() {
- // This CUJ don't work in shell transitions because of b/204570898 b/204562589 b/206753786
- assumeFalse(isShellTransitionsEnabled)
- }
-
/**
* Checks that all parts of the screen are covered at the start and end of the transition
*/
@@ -128,6 +121,8 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
@Presubmit
@Test
fun appLayerRotates_EndingBounds() {
+ // This CUJ don't work in shell transitions because of b/204570898 b/204562589 b/206753786
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayersEnd {
visibleRegion(fixedApp.component).coversExactly(screenBoundsEnd)
}
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 a3b98a8fc880..825320b4e784 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
@@ -37,6 +37,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
import android.content.Context;
import android.content.LocusId;
import android.content.pm.ParceledListSlice;
@@ -334,8 +335,7 @@ public class ShellTaskOrganizerTests {
mOrganizer.onTaskAppeared(taskInfo1, null);
// sizeCompatActivity is null if top activity is not in size compat.
- verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* taskListener */);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
// sizeCompatActivity is non-null if top activity is in size compat.
clearInvocations(mCompatUI);
@@ -345,8 +345,7 @@ public class ShellTaskOrganizerTests {
taskInfo2.topActivityInSizeCompat = true;
taskInfo2.isVisible = true;
mOrganizer.onTaskInfoChanged(taskInfo2);
- verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- taskInfo1.configuration, taskListener);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener);
// Not show size compat UI if task is not visible.
clearInvocations(mCompatUI);
@@ -356,13 +355,82 @@ public class ShellTaskOrganizerTests {
taskInfo3.topActivityInSizeCompat = true;
taskInfo3.isVisible = false;
mOrganizer.onTaskInfoChanged(taskInfo3);
- verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* taskListener */);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo3, null /* taskListener */);
clearInvocations(mCompatUI);
mOrganizer.onTaskVanished(taskInfo1);
- verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* taskListener */);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
+ }
+
+ @Test
+ public void testOnCameraCompatActivityChanged() {
+ final RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN);
+ taskInfo1.displayId = DEFAULT_DISPLAY;
+ taskInfo1.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+ final TrackingTaskListener taskListener = new TrackingTaskListener();
+ mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(taskInfo1, null);
+
+ // Task listener sent to compat UI is null if top activity doesn't request a camera
+ // compat control.
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
+
+ // Task linster is non-null when request a camera compat control for a visible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo2 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo2.displayId = taskInfo1.displayId;
+ taskInfo2.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ taskInfo2.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo2);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener);
+
+ // CompatUIController#onCompatInfoChanged is called when requested state for a camera
+ // compat control changes for a visible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo3 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo3.displayId = taskInfo1.displayId;
+ taskInfo3.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+ taskInfo3.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo3);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo3, taskListener);
+
+ // CompatUIController#onCompatInfoChanged is called when a top activity goes in size compat
+ // mode for a visible task that has a compat control.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo4 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo4.displayId = taskInfo1.displayId;
+ taskInfo4.topActivityInSizeCompat = true;
+ taskInfo4.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+ taskInfo4.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo4);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo4, taskListener);
+
+ // Task linster is null when a camera compat control is dimissed for a visible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo5 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo5.displayId = taskInfo1.displayId;
+ taskInfo5.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+ taskInfo5.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo5);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo5, null /* taskListener */);
+
+ // Task linster is null when request a camera compat control for a invisible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo6 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo6.displayId = taskInfo1.displayId;
+ taskInfo6.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ taskInfo6.isVisible = false;
+ mOrganizer.onTaskInfoChanged(taskInfo6);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo6, null /* taskListener */);
+
+ clearInvocations(mCompatUI);
+ mOrganizer.onTaskVanished(taskInfo1);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index 2b5cd601b200..51eec27cfc0e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -18,6 +18,7 @@ package com.android.wm.shell;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -36,6 +37,7 @@ public final class TestRunningTaskInfoBuilder {
private WindowContainerToken mToken = createMockWCToken();
private int mParentTaskId = INVALID_TASK_ID;
private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD;
+ private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED;
public static WindowContainerToken createMockWCToken() {
final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
@@ -60,6 +62,12 @@ public final class TestRunningTaskInfoBuilder {
return this;
}
+ public TestRunningTaskInfoBuilder setWindowingMode(
+ @WindowConfiguration.WindowingMode int windowingMode) {
+ mWindowingMode = windowingMode;
+ return this;
+ }
+
public ActivityManager.RunningTaskInfo build() {
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.parentTaskId = INVALID_TASK_ID;
@@ -67,6 +75,7 @@ public final class TestRunningTaskInfoBuilder {
info.parentTaskId = mParentTaskId;
info.configuration.windowConfiguration.setBounds(mBounds);
info.configuration.windowConfiguration.setActivityType(mActivityType);
+ info.configuration.windowConfiguration.setWindowingMode(mWindowingMode);
info.token = mToken;
info.isResizeable = true;
info.supportsMultiWindow = true;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index f622edb7f134..4352fd3d2c27 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -16,6 +16,10 @@
package com.android.wm.shell.compatui;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -29,6 +33,9 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
@@ -90,8 +97,8 @@ public class CompatUIControllerTest extends ShellTestCase {
mController = new CompatUIController(mContext, mMockDisplayController,
mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
@Override
- CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
- Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
+ CompatUIWindowManager createLayout(Context context, TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener) {
return mMockLayout;
}
};
@@ -106,23 +113,59 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testOnCompatInfoChanged() {
- final Configuration taskConfig = new Configuration();
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
// Verify that the restart button is added with non-null size compat info.
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig),
- eq(mMockTaskListener));
+ verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener));
// Verify that the restart button is updated with non-null new size compat info.
- final Configuration newTaskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
+ mMockTaskListener);
+
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Verify that the restart button is updated with new camera state.
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED),
+ mMockTaskListener);
+
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED),
+ mMockTaskListener);
+
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Verify that compat controls are removed with null compat info.
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
+ null /* taskListener */);
- verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
- true /* show */);
+ verify(mMockLayout).release();
+
+ clearInvocations(mMockLayout);
+ clearInvocations(mController);
+ // Verify that compat controls are removed with dismissed camera state.
+ taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
+
+ verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener));
- // Verify that the restart button is removed with null size compat info.
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_DISMISSED),
+ null /* taskListener */);
verify(mMockLayout).release();
}
@@ -139,8 +182,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testOnDisplayRemoved() {
mController.onDisplayAdded(DISPLAY_ID);
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
mMockTaskListener);
mController.onDisplayRemoved(DISPLAY_ID + 1);
@@ -157,16 +200,14 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testOnDisplayConfigurationChanged() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
- mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
- final Configuration newTaskConfig = new Configuration();
- mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, newTaskConfig);
+ mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, new Configuration());
verify(mMockLayout, never()).updateDisplayLayout(any());
- mController.onDisplayConfigurationChanged(DISPLAY_ID, newTaskConfig);
+ mController.onDisplayConfigurationChanged(DISPLAY_ID, new Configuration());
verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
}
@@ -174,9 +215,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testInsetsChanged() {
mController.onDisplayAdded(DISPLAY_ID);
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
- mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
InsetsState insetsState = new InsetsState();
InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
insetsSource.setFrame(0, 0, 1000, 1000);
@@ -196,8 +236,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testChangeButtonVisibilityOnImeShowHide() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
// Verify that the restart button is hidden after IME is showing.
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
@@ -205,10 +245,11 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while IME is showing.
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
- false /* show */);
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ false /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
// Verify button is shown after IME is hidden.
mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
@@ -218,8 +259,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
// Verify that the restart button is hidden after keyguard becomes occluded.
mController.onKeyguardOccludedChanged(true);
@@ -227,10 +268,11 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while keyguard is occluded.
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
- false /* show */);
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ false /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
// Verify button is shown after keyguard becomes not occluded.
mController.onKeyguardOccludedChanged(false);
@@ -240,8 +282,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
mController.onKeyguardOccludedChanged(true);
@@ -263,8 +305,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
mController.onKeyguardOccludedChanged(true);
@@ -283,4 +325,14 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(true);
}
+
+ private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
+ RunningTaskInfo taskInfo = new RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.displayId = displayId;
+ taskInfo.topActivityInSizeCompat = hasSizeCompat;
+ taskInfo.cameraCompatControlState = cameraCompatControlState;
+ return taskInfo;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index 2c3987bc358d..353d8fe8bc52 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -16,6 +16,11 @@
package com.android.wm.shell.compatui;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.mockito.Mockito.doNothing;
@@ -69,7 +74,7 @@ public class CompatUILayoutTest extends ShellTestCase {
mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
- false /* hasShownHint */);
+ false /* hasShownSizeCompatHint */, false /* hasShownCameraCompatHint */);
mCompatUILayout = (CompatUILayout)
LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
@@ -78,6 +83,7 @@ public class CompatUILayoutTest extends ShellTestCase {
spyOn(mWindowManager);
spyOn(mCompatUILayout);
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+ doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
}
@Test
@@ -86,7 +92,6 @@ public class CompatUILayoutTest extends ShellTestCase {
button.performClick();
verify(mWindowManager).onRestartButtonClicked();
- doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
}
@@ -102,10 +107,92 @@ public class CompatUILayoutTest extends ShellTestCase {
@Test
public void testOnClickForSizeCompatHint() {
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint);
sizeCompatHint.performClick();
verify(mCompatUILayout).setSizeCompatHintVisibility(/* show= */ false);
}
+
+ @Test
+ public void testUpdateCameraTreatmentButton_treatmentAppliedByDefault() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
+ button.performClick();
+
+ verify(mWindowManager).onCameraTreatmentButtonClicked();
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ button.performClick();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ }
+
+ @Test
+ public void testUpdateCameraTreatmentButton_treatmentSuggestedByDefault() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
+ button.performClick();
+
+ verify(mWindowManager).onCameraTreatmentButtonClicked();
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ button.performClick();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ }
+
+ @Test
+ public void testOnCameraDismissButtonClicked() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button);
+ button.performClick();
+
+ verify(mWindowManager).onCameraDismissButtonClicked();
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_DISMISSED);
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ false);
+ }
+
+ @Test
+ public void testOnLongClickForCameraTreatementButton() {
+ doNothing().when(mWindowManager).onCameraButtonLongClicked();
+
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
+ button.performLongClick();
+
+ verify(mWindowManager).onCameraButtonLongClicked();
+ }
+
+ @Test
+ public void testOnLongClickForCameraDismissButton() {
+ doNothing().when(mWindowManager).onCameraButtonLongClicked();
+
+ final ImageButton button = mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button);
+ button.performLongClick();
+
+ verify(mWindowManager).onCameraButtonLongClicked();
+ }
+
+ @Test
+ public void testOnClickForCameraCompatHint() {
+ mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ final LinearLayout hint = mCompatUILayout.findViewById(R.id.camera_compat_hint);
+ hint.performClick();
+
+ verify(mCompatUILayout).setCameraCompatHintVisibility(/* show= */ false);
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index d5dcf2e11a46..11c797363819 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -16,6 +16,10 @@
package com.android.wm.shell.compatui;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -23,6 +27,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -81,7 +86,7 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
- false /* hasShownHint */);
+ false /* hasShownSizeCompatHint */, false /* hasShownSizeCompatHint */);
spyOn(mWindowManager);
doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
@@ -91,31 +96,35 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testCreateSizeCompatButton() {
// Not create layout if show is false.
- mWindowManager.createLayout(false /* show */);
+ mWindowManager.createLayout(false /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager, never()).inflateCompatUILayout();
// Not create hint popup.
- mWindowManager.mShouldShowHint = false;
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.mShouldShowSizeCompatHint = false;
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager).inflateCompatUILayout();
- verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
+ verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
// Create hint popup.
mWindowManager.release();
- mWindowManager.mShouldShowHint = true;
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.mShouldShowSizeCompatHint = true;
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager, times(2)).inflateCompatUILayout();
assertNotNull(mCompatUILayout);
verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
- assertFalse(mWindowManager.mShouldShowHint);
+ assertFalse(mWindowManager.mShouldShowSizeCompatHint);
}
@Test
public void testRelease() {
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager).inflateCompatUILayout();
@@ -126,32 +135,60 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testUpdateCompatInfo() {
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
// No diff
clearInvocations(mWindowManager);
- mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */);
+ mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager, never()).updateSurfacePosition();
verify(mWindowManager, never()).release();
- verify(mWindowManager, never()).createLayout(anyBoolean());
+ verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
// Change task listener, recreate button.
clearInvocations(mWindowManager);
final ShellTaskOrganizer.TaskListener newTaskListener = mock(
ShellTaskOrganizer.TaskListener.class);
mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
- true /* show */);
+ true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager).release();
- verify(mWindowManager).createLayout(anyBoolean());
+ verify(mWindowManager).createLayout(anyBoolean(), anyBoolean(), anyInt());
+
+ // Change Camera Compat state, show a control.
+ mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, true /* show */,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ // Change Camera Compat state, update a control.
+ mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, true /* show */,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ // Change Camera Compat state to hidden, hide a control.
+ mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
+ true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ false);
// Change task bounds, update position.
clearInvocations(mWindowManager);
final Configuration newTaskConfiguration = new Configuration();
newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener,
- true /* show */);
+ true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager).updateSurfacePosition();
}
@@ -201,23 +238,25 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
public void testUpdateVisibility() {
// Create button if it is not created.
mWindowManager.mCompatUILayout = null;
+ mWindowManager.mHasSizeCompat = true;
mWindowManager.updateVisibility(true /* show */);
- verify(mWindowManager).createLayout(true /* show */);
+ verify(mWindowManager).createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
// Hide button.
clearInvocations(mWindowManager);
doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility();
mWindowManager.updateVisibility(false /* show */);
- verify(mWindowManager, never()).createLayout(anyBoolean());
+ verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
verify(mCompatUILayout).setVisibility(View.GONE);
// Show button.
doReturn(View.GONE).when(mCompatUILayout).getVisibility();
mWindowManager.updateVisibility(true /* show */);
- verify(mWindowManager, never()).createLayout(anyBoolean());
+ verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
verify(mCompatUILayout).setVisibility(View.VISIBLE);
}
@@ -230,6 +269,37 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
}
@Test
+ public void testOnCameraDismissButtonClicked() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ clearInvocations(mCompatUILayout);
+ mWindowManager.onCameraDismissButtonClicked();
+
+ verify(mCallback).onCameraControlStateUpdated(TASK_ID, CAMERA_COMPAT_CONTROL_DISMISSED);
+ verify(mCompatUILayout).setCameraControlVisibility(/* show= */ false);
+ }
+
+ @Test
+ public void testOnCameraTreatmentButtonClicked() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ clearInvocations(mCompatUILayout);
+ mWindowManager.onCameraTreatmentButtonClicked();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ mWindowManager.onCameraTreatmentButtonClicked();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ }
+
+ @Test
public void testOnRestartButtonClicked() {
mWindowManager.onRestartButtonClicked();
@@ -239,15 +309,60 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testOnRestartButtonLongClicked_showHint() {
// Not create hint popup.
- mWindowManager.mShouldShowHint = false;
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.mShouldShowSizeCompatHint = false;
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager).inflateCompatUILayout();
- verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
+ verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
mWindowManager.onRestartButtonLongClicked();
verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
}
+ @Test
+ public void testOnCamerControlLongClicked_showHint() {
+ // Not create hint popup.
+ mWindowManager.mShouldShowCameraCompatHint = false;
+ mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mWindowManager).inflateCompatUILayout();
+ verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
+
+ mWindowManager.onCameraButtonLongClicked();
+
+ verify(mCompatUILayout).setCameraCompatHintVisibility(true /* show */);
+ }
+
+ @Test
+ public void testCreateCameraCompatControl() {
+ // Not create layout if show is false.
+ mWindowManager.createLayout(false /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mWindowManager, never()).inflateCompatUILayout();
+
+ // Not create hint popup.
+ mWindowManager.mShouldShowCameraCompatHint = false;
+ mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mWindowManager).inflateCompatUILayout();
+ verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
+ verify(mCompatUILayout).setCameraControlVisibility(true /* show */);
+
+ // Create hint popup.
+ mWindowManager.release();
+ mWindowManager.mShouldShowCameraCompatHint = true;
+ mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mWindowManager, times(2)).inflateCompatUILayout();
+ assertNotNull(mCompatUILayout);
+ verify(mCompatUILayout, times(2)).setCameraControlVisibility(true /* show */);
+ assertFalse(mWindowManager.mShouldShowCameraCompatHint);
+ }
+
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index aab1e3a99c98..dda1a8295e51 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -31,6 +31,7 @@ import android.window.WindowContainerToken;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
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.DisplayInsetsController;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -69,15 +70,15 @@ public class SplitTestUtils {
TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
- MainStage mainStage, SideStage sideStage, DisplayImeController imeController,
- DisplayInsetsController insetsController, SplitLayout splitLayout,
- Transitions transitions, TransactionPool transactionPool,
+ MainStage mainStage, SideStage sideStage, DisplayController displayController,
+ DisplayImeController imeController, DisplayInsetsController insetsController,
+ SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger,
Optional<RecentTasksController> recentTasks,
Provider<Optional<StageTaskUnfoldController>> unfoldController) {
super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage,
- sideStage, imeController, insetsController, splitLayout, transitions,
- transactionPool, logger, recentTasks, unfoldController);
+ sideStage, displayController, imeController, insetsController, splitLayout,
+ transitions, transactionPool, logger, recentTasks, unfoldController);
// Prepare default TaskDisplayArea for testing.
mDisplayAreaInfo = new DisplayAreaInfo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index c853fd350986..ea94cf0f7597 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.splitscreen;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -26,6 +27,9 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitTestUtils.createMockSurface;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
@@ -61,6 +65,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
@@ -85,6 +90,7 @@ public class SplitTransitionTests extends ShellTestCase {
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
+ @Mock private DisplayController mDisplayController;
@Mock private DisplayImeController mDisplayImeController;
@Mock private DisplayInsetsController mDisplayInsetsController;
@Mock private TransactionPool mTransactionPool;
@@ -120,8 +126,8 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
- mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
- mTransactionPool, mLogger, Optional.empty(), Optional::empty);
+ mDisplayController, mDisplayImeController, mDisplayInsetsController, mSplitLayout,
+ mTransitions, mTransactionPool, mLogger, Optional.empty(), Optional::empty);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
@@ -249,7 +255,9 @@ public class SplitTransitionTests extends ShellTestCase {
enterSplit();
ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
- .setActivityType(ACTIVITY_TYPE_HOME).build();
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setActivityType(ACTIVITY_TYPE_HOME)
+ .build();
// Create a request to bring home forward
TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask, null);
@@ -280,6 +288,64 @@ public class SplitTransitionTests extends ShellTestCase {
}
@Test
+ public void testDismissFromBeingOccluded() {
+ enterSplit();
+
+ ActivityManager.RunningTaskInfo normalTask = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .build();
+
+ // Create a request to bring a normal task forward
+ TransitionRequestInfo request =
+ new TransitionRequestInfo(TRANSIT_TO_FRONT, normalTask, null);
+ IBinder transition = mock(IBinder.class);
+ WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
+
+ assertTrue(containsSplitExit(result));
+
+ // make sure we haven't made any local changes yet (need to wait until transition is ready)
+ assertTrue(mStageCoordinator.isSplitScreenVisible());
+
+ // simulate the transition
+ TransitionInfo.Change normalChange = createChange(TRANSIT_TO_FRONT, normalTask);
+ TransitionInfo.Change mainChange = createChange(TRANSIT_TO_BACK, mMainChild);
+ TransitionInfo.Change sideChange = createChange(TRANSIT_TO_BACK, mSideChild);
+
+ TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0);
+ info.addChange(normalChange);
+ info.addChange(mainChange);
+ info.addChange(sideChange);
+ mMainStage.onTaskVanished(mMainChild);
+ mSideStage.onTaskVanished(mSideChild);
+ mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertFalse(mStageCoordinator.isSplitScreenVisible());
+ }
+
+ @Test
+ public void testDismissFromMultiWindowSupport() {
+ enterSplit();
+
+ // simulate the transition
+ TransitionInfo.Change mainChange = createChange(TRANSIT_TO_BACK, mMainChild);
+ TransitionInfo.Change sideChange = createChange(TRANSIT_TO_BACK, mSideChild);
+ TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
+ info.addChange(mainChange);
+ info.addChange(sideChange);
+ IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
+ new WindowContainerTransaction(), mStageCoordinator,
+ EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, STAGE_TYPE_SIDE);
+ boolean accepted = mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertTrue(accepted);
+ assertFalse(mStageCoordinator.isSplitScreenVisible());
+ }
+
+ @Test
public void testDismissSnap() {
enterSplit();
@@ -290,8 +356,9 @@ public class SplitTransitionTests extends ShellTestCase {
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
info.addChange(mainChange);
info.addChange(sideChange);
- IBinder transition = mStageCoordinator.onSnappedToDismissTransition(
- false /* mainStageToTop */);
+ IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
+ new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_DRAG_DIVIDER,
+ STAGE_TYPE_SIDE);
mMainStage.onTaskVanished(mMainChild);
mSideStage.onTaskVanished(mSideChild);
boolean accepted = mStageCoordinator.startAnimation(transition, info,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index fb6300c8a4bf..099987a2f821 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -51,6 +51,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -91,6 +92,8 @@ public class StageCoordinatorTests extends ShellTestCase {
@Mock
private SplitLayout mSplitLayout;
@Mock
+ private DisplayController mDisplayController;
+ @Mock
private DisplayImeController mDisplayImeController;
@Mock
private DisplayInsetsController mDisplayInsetsController;
@@ -293,7 +296,7 @@ public class StageCoordinatorTests extends ShellTestCase {
private StageCoordinator createStageCoordinator(SplitLayout splitLayout) {
return new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
- mDisplayImeController, mDisplayInsetsController, splitLayout,
+ mDisplayController, mDisplayImeController, mDisplayInsetsController, splitLayout,
mTransitions, mTransactionPool, mLogger, Optional.empty(),
new UnfoldControllerProvider());
}
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 1b3892032e79..c4366f756a21 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -273,21 +273,99 @@ static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
return ret;
}
-static void RuntimeShader_updateUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
- jstring jUniformName, jfloatArray jvalues) {
+static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
+ switch (type) {
+ case SkRuntimeEffect::Uniform::Type::kFloat:
+ case SkRuntimeEffect::Uniform::Type::kFloat2:
+ case SkRuntimeEffect::Uniform::Type::kFloat3:
+ case SkRuntimeEffect::Uniform::Type::kFloat4:
+ case SkRuntimeEffect::Uniform::Type::kFloat2x2:
+ case SkRuntimeEffect::Uniform::Type::kFloat3x3:
+ case SkRuntimeEffect::Uniform::Type::kFloat4x4:
+ return false;
+ case SkRuntimeEffect::Uniform::Type::kInt:
+ case SkRuntimeEffect::Uniform::Type::kInt2:
+ case SkRuntimeEffect::Uniform::Type::kInt3:
+ case SkRuntimeEffect::Uniform::Type::kInt4:
+ return true;
+ }
+}
+
+static void UpdateFloatUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder,
+ const char* uniformName, const float values[], int count,
+ bool isColor) {
+ SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName);
+ if (uniform.fVar == nullptr) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+ } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
+ if (isColor) {
+ jniThrowExceptionFmt(
+ env, "java/lang/IllegalArgumentException",
+ "attempting to set a color uniform using the non-color specific APIs: %s %x",
+ uniformName, uniform.fVar->flags);
+ } else {
+ ThrowIAEFmt(env,
+ "attempting to set a non-color uniform using the setColorUniform APIs: %s",
+ uniformName);
+ }
+ } else if (isIntUniformType(uniform.fVar->type)) {
+ ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
+ uniformName);
+ } else if (!uniform.set<float>(values, count)) {
+ ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
+ }
+}
+
+static void RuntimeShader_updateFloatUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jfloat value1, jfloat value2,
+ jfloat value3, jfloat value4, jint count) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ const float values[4] = {value1, value2, value3, value4};
+ UpdateFloatUniforms(env, builder, name.c_str(), values, count, false);
+}
+
+static void RuntimeShader_updateFloatArrayUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jfloatArray jvalues,
+ jboolean isColor) {
SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
ScopedUtfChars name(env, jUniformName);
AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
+ UpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(), isColor);
+}
- SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(name.c_str());
+static void UpdateIntUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder, const char* uniformName,
+ const int values[], int count) {
+ SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName);
if (uniform.fVar == nullptr) {
- ThrowIAEFmt(env, "unable to find uniform named %s", name.c_str());
- } else if (!uniform.set<float>(autoValues.ptr(), autoValues.length())) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+ } else if (!isIntUniformType(uniform.fVar->type)) {
+ ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
+ uniformName);
+ } else if (!uniform.set<int>(values, count)) {
ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
- uniform.fVar->sizeInBytes(), sizeof(float) * autoValues.length());
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
}
}
+static void RuntimeShader_updateIntUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jint value1, jint value2,
+ jint value3, jint value4, jint count) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ const int values[4] = {value1, value2, value3, value4};
+ UpdateIntUniforms(env, builder, name.c_str(), values, count);
+}
+
+static void RuntimeShader_updateIntArrayUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jintArray jvalues) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ AutoJavaIntArray autoValues(env, jvalues, 0);
+ UpdateIntUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length());
+}
+
static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder,
jstring jUniformName, jlong shaderHandle) {
SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
@@ -338,7 +416,14 @@ static const JNINativeMethod gRuntimeShaderMethods[] = {
{"nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer},
{"nativeCreateShader", "(JJZ)J", (void*)RuntimeShader_create},
{"nativeCreateBuilder", "(Ljava/lang/String;)J", (void*)RuntimeShader_createShaderBuilder},
- {"nativeUpdateUniforms", "(JLjava/lang/String;[F)V", (void*)RuntimeShader_updateUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V",
+ (void*)RuntimeShader_updateFloatArrayUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V",
+ (void*)RuntimeShader_updateFloatUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V",
+ (void*)RuntimeShader_updateIntArrayUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
+ (void*)RuntimeShader_updateIntUniforms},
{"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader},
};
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 2f8ddeecced0..02257db9df6a 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -262,8 +262,6 @@ EGLConfig EglManager::loadA8Config(EGLDisplay display, EglManager::SwapBehavior
0,
EGL_DEPTH_SIZE,
0,
- EGL_STENCIL_SIZE,
- STENCIL_BUFFER_SIZE,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT | eglSwapBehavior,
EGL_NONE};
@@ -354,10 +352,6 @@ void EglManager::loadConfigs() {
ALOGW("Failed to initialize 101010-2 format, error = %s",
eglErrorString());
}
- mEglConfigA8 = loadA8Config(mEglDisplay, mSwapBehavior);
- if (mEglConfigA8 == EGL_NO_CONFIG_KHR) {
- ALOGE("Failed to initialize A8 format, error = %s", eglErrorString());
- }
}
void EglManager::createContext() {
@@ -431,9 +425,13 @@ Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window,
EGLConfig config = mEglConfig;
if (colorMode == ColorMode::A8) {
// A8 doesn't use a color space
+ if (!mEglConfigA8) {
+ mEglConfigA8 = loadA8Config(mEglDisplay, mSwapBehavior);
+ LOG_ALWAYS_FATAL_IF(!mEglConfigA8,
+ "Requested ColorMode::A8, but EGL lacks support! error = %s",
+ eglErrorString());
+ }
config = mEglConfigA8;
-
- LOG_ALWAYS_FATAL_IF(!mEglConfigA8, "Requested ColorMode::A8, but EGL lacks support!");
} else {
if (!mHasWideColorGamutSupport) {
colorMode = ColorMode::Default;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 0722417c0d18..dd27cf140ca1 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6788,56 +6788,63 @@ public class AudioManager {
/**
* Returns a list of audio formats that corresponds to encoding formats
- * supported on offload path for A2DP and LE audio playback.
+ * supported on offload path for A2DP playback.
*
- * @param deviceType Indicates the target device type {@link AudioSystem.DeviceType}
* @return a list of {@link BluetoothCodecConfig} objects containing encoding formats
- * supported for offload A2DP playback or a list of {@link BluetoothLeAudioCodecConfig}
- * objects containing encoding formats supported for offload LE Audio playback
+ * supported for offload A2DP playback
* @hide
*/
- public List<?> getHwOffloadFormatsSupportedForBluetoothMedia(
- @AudioSystem.DeviceType int deviceType) {
- ArrayList<Integer> formatsList = new ArrayList<Integer>();
- ArrayList<BluetoothCodecConfig> a2dpCodecConfigList = new ArrayList<BluetoothCodecConfig>();
- ArrayList<BluetoothLeAudioCodecConfig> leAudioCodecConfigList =
- new ArrayList<BluetoothLeAudioCodecConfig>();
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public @NonNull List<BluetoothCodecConfig> getHwOffloadFormatsSupportedForA2dp() {
+ ArrayList<Integer> formatsList = new ArrayList<>();
+ ArrayList<BluetoothCodecConfig> codecConfigList = new ArrayList<>();
- if (deviceType != AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP
- && deviceType != AudioSystem.DEVICE_OUT_BLE_HEADSET) {
- throw new IllegalArgumentException(
- "Illegal devicetype for the getHwOffloadFormatsSupportedForBluetoothMedia");
+ int status = AudioSystem.getHwOffloadFormatsSupportedForBluetoothMedia(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, formatsList);
+ if (status != AudioManager.SUCCESS) {
+ Log.e(TAG, "getHwOffloadEncodingFormatsSupportedForA2DP failed:" + status);
+ return codecConfigList;
+ }
+
+ for (Integer format : formatsList) {
+ int btSourceCodec = AudioSystem.audioFormatToBluetoothSourceCodec(format);
+ if (btSourceCodec != BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
+ codecConfigList.add(new BluetoothCodecConfig(btSourceCodec));
+ }
}
+ return codecConfigList;
+ }
+
+ /**
+ * Returns a list of audio formats that corresponds to encoding formats
+ * supported on offload path for Le audio playback.
+ *
+ * @return a list of {@link BluetoothLeAudioCodecConfig} objects containing encoding formats
+ * supported for offload Le Audio playback
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @NonNull
+ public List<BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio() {
+ ArrayList<Integer> formatsList = new ArrayList<>();
+ ArrayList<BluetoothLeAudioCodecConfig> leAudioCodecConfigList = new ArrayList<>();
- int status = AudioSystem.getHwOffloadFormatsSupportedForBluetoothMedia(deviceType,
- formatsList);
+ int status = AudioSystem.getHwOffloadFormatsSupportedForBluetoothMedia(
+ AudioSystem.DEVICE_OUT_BLE_HEADSET, formatsList);
if (status != AudioManager.SUCCESS) {
- Log.e(TAG, "getHwOffloadFormatsSupportedForBluetoothMedia for deviceType "
- + deviceType + " failed:" + status);
- return a2dpCodecConfigList;
+ Log.e(TAG, "getHwOffloadEncodingFormatsSupportedForLeAudio failed:" + status);
+ return leAudioCodecConfigList;
}
- if (deviceType == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
- for (Integer format : formatsList) {
- int btSourceCodec = AudioSystem.audioFormatToBluetoothSourceCodec(format);
- if (btSourceCodec != BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
- a2dpCodecConfigList.add(new BluetoothCodecConfig(btSourceCodec));
- }
+ for (Integer format : formatsList) {
+ int btLeAudioCodec = AudioSystem.audioFormatToBluetoothLeAudioSourceCodec(format);
+ if (btLeAudioCodec != BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
+ leAudioCodecConfigList.add(new BluetoothLeAudioCodecConfig.Builder()
+ .setCodecType(btLeAudioCodec)
+ .build());
}
- return a2dpCodecConfigList;
- } else if (deviceType == AudioSystem.DEVICE_OUT_BLE_HEADSET) {
- for (Integer format : formatsList) {
- int btLeAudioCodec = AudioSystem.audioFormatToBluetoothLeAudioSourceCodec(format);
- if (btLeAudioCodec != BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
- leAudioCodecConfigList.add(new BluetoothLeAudioCodecConfig.Builder()
- .setCodecType(btLeAudioCodec)
- .build());
- }
- }
- return leAudioCodecConfigList;
}
- Log.e(TAG, "Input deviceType " + deviceType + " doesn't support.");
- return a2dpCodecConfigList;
+ return leAudioCodecConfigList;
}
// Since we need to calculate the changes since THE LAST NOTIFICATION, and not since the
diff --git a/media/java/android/media/MediaActionSound.java b/media/java/android/media/MediaActionSound.java
index dcd4dce5f3eb..ec56d614f2b5 100644
--- a/media/java/android/media/MediaActionSound.java
+++ b/media/java/android/media/MediaActionSound.java
@@ -16,8 +16,11 @@
package android.media;
-import android.media.AudioManager;
+import android.content.Context;
import android.media.SoundPool;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
/**
@@ -104,6 +107,26 @@ public class MediaActionSound {
private static final int STATE_LOADING_PLAY_REQUESTED = 2;
private static final int STATE_LOADED = 3;
+ /**
+ * <p>Returns true if the application must play the shutter sound in accordance
+ * to certain regional restrictions. </p>
+ *
+ * <p>If this method returns true, applications are strongly recommended to use
+ * MediaActionSound.play(SHUTTER_CLICK) or START_VIDEO_RECORDING whenever it captures
+ * images or video to storage or sends them over the network.</p>
+ */
+ public static boolean mustPlayShutterSound() {
+ boolean result = false;
+ IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+ IAudioService audioService = IAudioService.Stub.asInterface(b);
+ try {
+ result = audioService.isCameraSoundForced();
+ } catch (RemoteException e) {
+ Log.e(TAG, "audio service is unavailable for queries, defaulting to false");
+ }
+ return result;
+ }
+
private class SoundState {
public final int name;
public int id;
diff --git a/media/java/android/media/tv/BroadcastInfoRequest.java b/media/java/android/media/tv/BroadcastInfoRequest.java
index c439356ef184..85ad3cdb5700 100644
--- a/media/java/android/media/tv/BroadcastInfoRequest.java
+++ b/media/java/android/media/tv/BroadcastInfoRequest.java
@@ -16,38 +16,41 @@
package android.media.tv;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
-import android.annotation.NonNull;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/** @hide */
public abstract class BroadcastInfoRequest implements Parcelable {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({REQUEST_OPTION_REPEAT, REQUEST_OPTION_AUTO_UPDATE})
+ public @interface RequestOption {}
- // todo: change const declaration to intdef
- public static final int REQUEST_OPTION_REPEAT = 11;
- public static final int REQUEST_OPTION_AUTO_UPDATE = 12;
+ public static final int REQUEST_OPTION_REPEAT = 0;
+ public static final int REQUEST_OPTION_AUTO_UPDATE = 1;
public static final @NonNull Parcelable.Creator<BroadcastInfoRequest> CREATOR =
new Parcelable.Creator<BroadcastInfoRequest>() {
@Override
public BroadcastInfoRequest createFromParcel(Parcel source) {
- int type = source.readInt();
+ @TvInputManager.BroadcastInfoType int type = source.readInt();
switch (type) {
- case BroadcastInfoType.TS:
+ case TvInputManager.BROADCAST_INFO_TYPE_TS:
return TsRequest.createFromParcelBody(source);
- case BroadcastInfoType.TABLE:
- return TableRequest.createFromParcelBody(source);
- case BroadcastInfoType.SECTION:
+ case TvInputManager.BROADCAST_INFO_TYPE_SECTION:
return SectionRequest.createFromParcelBody(source);
- case BroadcastInfoType.PES:
+ case TvInputManager.BROADCAST_INFO_TYPE_PES:
return PesRequest.createFromParcelBody(source);
- case BroadcastInfoType.STREAM_EVENT:
+ case TvInputManager.BROADCAST_INFO_STREAM_EVENT:
return StreamEventRequest.createFromParcelBody(source);
- case BroadcastInfoType.DSMCC:
+ case TvInputManager.BROADCAST_INFO_TYPE_DSMCC:
return DsmccRequest.createFromParcelBody(source);
- case BroadcastInfoType.TV_PROPRIETARY_FUNCTION:
- return TvProprietaryFunctionRequest.createFromParcelBody(source);
+ case TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION:
+ return CommandRequest.createFromParcelBody(source);
default:
throw new IllegalStateException(
"Unexpected broadcast info request type (value "
@@ -61,23 +64,24 @@ public abstract class BroadcastInfoRequest implements Parcelable {
}
};
- protected final int mType;
+ protected final @TvInputManager.BroadcastInfoType int mType;
protected final int mRequestId;
- protected final int mOption;
+ protected final @RequestOption int mOption;
- protected BroadcastInfoRequest(int type, int requestId, int option) {
+ protected BroadcastInfoRequest(@TvInputManager.BroadcastInfoType int type,
+ int requestId, @RequestOption int option) {
mType = type;
mRequestId = requestId;
mOption = option;
}
- protected BroadcastInfoRequest(int type, Parcel source) {
+ protected BroadcastInfoRequest(@TvInputManager.BroadcastInfoType int type, Parcel source) {
mType = type;
mRequestId = source.readInt();
mOption = source.readInt();
}
- public int getType() {
+ public @TvInputManager.BroadcastInfoType int getType() {
return mType;
}
@@ -85,7 +89,7 @@ public abstract class BroadcastInfoRequest implements Parcelable {
return mRequestId;
}
- public int getOption() {
+ public @RequestOption int getOption() {
return mOption;
}
diff --git a/media/java/android/media/tv/BroadcastInfoResponse.java b/media/java/android/media/tv/BroadcastInfoResponse.java
index 288f2f97e3b9..e423abaf550d 100644
--- a/media/java/android/media/tv/BroadcastInfoResponse.java
+++ b/media/java/android/media/tv/BroadcastInfoResponse.java
@@ -16,38 +16,42 @@
package android.media.tv;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
-import android.annotation.NonNull;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/** @hide */
public abstract class BroadcastInfoResponse implements Parcelable {
- // todo: change const declaration to intdef
- public static final int ERROR = 1;
- public static final int OK = 2;
- public static final int CANCEL = 3;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RESPONSE_RESULT_ERROR, RESPONSE_RESULT_OK, RESPONSE_RESULT_CANCEL})
+ public @interface ResponseResult {}
+
+ public static final int RESPONSE_RESULT_ERROR = 1;
+ public static final int RESPONSE_RESULT_OK = 2;
+ public static final int RESPONSE_RESULT_CANCEL = 3;
public static final @NonNull Parcelable.Creator<BroadcastInfoResponse> CREATOR =
new Parcelable.Creator<BroadcastInfoResponse>() {
@Override
public BroadcastInfoResponse createFromParcel(Parcel source) {
- int type = source.readInt();
+ @TvInputManager.BroadcastInfoType int type = source.readInt();
switch (type) {
- case BroadcastInfoType.TS:
+ case TvInputManager.BROADCAST_INFO_TYPE_TS:
return TsResponse.createFromParcelBody(source);
- case BroadcastInfoType.TABLE:
- return TableResponse.createFromParcelBody(source);
- case BroadcastInfoType.SECTION:
+ case TvInputManager.BROADCAST_INFO_TYPE_SECTION:
return SectionResponse.createFromParcelBody(source);
- case BroadcastInfoType.PES:
+ case TvInputManager.BROADCAST_INFO_TYPE_PES:
return PesResponse.createFromParcelBody(source);
- case BroadcastInfoType.STREAM_EVENT:
+ case TvInputManager.BROADCAST_INFO_STREAM_EVENT:
return StreamEventResponse.createFromParcelBody(source);
- case BroadcastInfoType.DSMCC:
+ case TvInputManager.BROADCAST_INFO_TYPE_DSMCC:
return DsmccResponse.createFromParcelBody(source);
- case BroadcastInfoType.TV_PROPRIETARY_FUNCTION:
- return TvProprietaryFunctionResponse.createFromParcelBody(source);
+ case TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION:
+ return CommandResponse.createFromParcelBody(source);
default:
throw new IllegalStateException(
"Unexpected broadcast info response type (value "
@@ -61,26 +65,27 @@ public abstract class BroadcastInfoResponse implements Parcelable {
}
};
- protected final int mType;
+ protected final @TvInputManager.BroadcastInfoType int mType;
protected final int mRequestId;
protected final int mSequence;
- protected final int mResponseResult;
+ protected final @ResponseResult int mResponseResult;
- protected BroadcastInfoResponse(int type, int requestId, int sequence, int responseResult) {
+ protected BroadcastInfoResponse(@TvInputManager.BroadcastInfoType int type, int requestId,
+ int sequence, @ResponseResult int responseResult) {
mType = type;
mRequestId = requestId;
mSequence = sequence;
mResponseResult = responseResult;
}
- protected BroadcastInfoResponse(int type, Parcel source) {
+ protected BroadcastInfoResponse(@TvInputManager.BroadcastInfoType int type, Parcel source) {
mType = type;
mRequestId = source.readInt();
mSequence = source.readInt();
mResponseResult = source.readInt();
}
- public int getType() {
+ public @TvInputManager.BroadcastInfoType int getType() {
return mType;
}
@@ -92,7 +97,7 @@ public abstract class BroadcastInfoResponse implements Parcelable {
return mSequence;
}
- public int getResponseResult() {
+ public @ResponseResult int getResponseResult() {
return mResponseResult;
}
diff --git a/media/java/android/media/tv/TvProprietaryFunctionRequest.java b/media/java/android/media/tv/CommandRequest.java
index 845641d4d6ca..2391fa3a4aef 100644
--- a/media/java/android/media/tv/TvProprietaryFunctionRequest.java
+++ b/media/java/android/media/tv/CommandRequest.java
@@ -21,20 +21,21 @@ import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class TvProprietaryFunctionRequest extends BroadcastInfoRequest implements Parcelable {
- public static final int requestType = BroadcastInfoType.TV_PROPRIETARY_FUNCTION;
+public final class CommandRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int requestType =
+ TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION;
- public static final @NonNull Parcelable.Creator<TvProprietaryFunctionRequest> CREATOR =
- new Parcelable.Creator<TvProprietaryFunctionRequest>() {
+ public static final @NonNull Parcelable.Creator<CommandRequest> CREATOR =
+ new Parcelable.Creator<CommandRequest>() {
@Override
- public TvProprietaryFunctionRequest createFromParcel(Parcel source) {
+ public CommandRequest createFromParcel(Parcel source) {
source.readInt();
return createFromParcelBody(source);
}
@Override
- public TvProprietaryFunctionRequest[] newArray(int size) {
- return new TvProprietaryFunctionRequest[size];
+ public CommandRequest[] newArray(int size) {
+ return new CommandRequest[size];
}
};
@@ -42,11 +43,11 @@ public class TvProprietaryFunctionRequest extends BroadcastInfoRequest implement
private final String mName;
private final String mArguments;
- public static TvProprietaryFunctionRequest createFromParcelBody(Parcel in) {
- return new TvProprietaryFunctionRequest(in);
+ public static CommandRequest createFromParcelBody(Parcel in) {
+ return new CommandRequest(in);
}
- public TvProprietaryFunctionRequest(int requestId, int option, String nameSpace,
+ public CommandRequest(int requestId, @RequestOption int option, String nameSpace,
String name, String arguments) {
super(requestType, requestId, option);
mNameSpace = nameSpace;
@@ -54,7 +55,7 @@ public class TvProprietaryFunctionRequest extends BroadcastInfoRequest implement
mArguments = arguments;
}
- protected TvProprietaryFunctionRequest(Parcel source) {
+ protected CommandRequest(Parcel source) {
super(requestType, source);
mNameSpace = source.readString();
mName = source.readString();
diff --git a/media/java/android/media/tv/TvProprietaryFunctionResponse.java b/media/java/android/media/tv/CommandResponse.java
index 3181b08c62f7..d34681f443c2 100644
--- a/media/java/android/media/tv/TvProprietaryFunctionResponse.java
+++ b/media/java/android/media/tv/CommandResponse.java
@@ -21,36 +21,37 @@ import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class TvProprietaryFunctionResponse extends BroadcastInfoResponse implements Parcelable {
- public static final int responseType = BroadcastInfoType.TV_PROPRIETARY_FUNCTION;
+public final class CommandResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int responseType =
+ TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION;
- public static final @NonNull Parcelable.Creator<TvProprietaryFunctionResponse> CREATOR =
- new Parcelable.Creator<TvProprietaryFunctionResponse>() {
+ public static final @NonNull Parcelable.Creator<CommandResponse> CREATOR =
+ new Parcelable.Creator<CommandResponse>() {
@Override
- public TvProprietaryFunctionResponse createFromParcel(Parcel source) {
+ public CommandResponse createFromParcel(Parcel source) {
source.readInt();
return createFromParcelBody(source);
}
@Override
- public TvProprietaryFunctionResponse[] newArray(int size) {
- return new TvProprietaryFunctionResponse[size];
+ public CommandResponse[] newArray(int size) {
+ return new CommandResponse[size];
}
};
private final String mResponse;
- public static TvProprietaryFunctionResponse createFromParcelBody(Parcel in) {
- return new TvProprietaryFunctionResponse(in);
+ public static CommandResponse createFromParcelBody(Parcel in) {
+ return new CommandResponse(in);
}
- public TvProprietaryFunctionResponse(int requestId, int sequence, int responseResult,
- String response) {
+ public CommandResponse(int requestId, int sequence,
+ @ResponseResult int responseResult, String response) {
super(responseType, requestId, sequence, responseResult);
mResponse = response;
}
- protected TvProprietaryFunctionResponse(Parcel source) {
+ protected CommandResponse(Parcel source) {
super(responseType, source);
mResponse = source.readString();
}
diff --git a/media/java/android/media/tv/DsmccRequest.java b/media/java/android/media/tv/DsmccRequest.java
index f2e47505ad15..6bb147287833 100644
--- a/media/java/android/media/tv/DsmccRequest.java
+++ b/media/java/android/media/tv/DsmccRequest.java
@@ -22,8 +22,9 @@ import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class DsmccRequest extends BroadcastInfoRequest implements Parcelable {
- public static final int requestType = BroadcastInfoType.DSMCC;
+public final class DsmccRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int requestType =
+ TvInputManager.BROADCAST_INFO_TYPE_DSMCC;
public static final @NonNull Parcelable.Creator<DsmccRequest> CREATOR =
new Parcelable.Creator<DsmccRequest>() {
@@ -45,7 +46,7 @@ public class DsmccRequest extends BroadcastInfoRequest implements Parcelable {
return new DsmccRequest(in);
}
- public DsmccRequest(int requestId, int option, Uri uri) {
+ public DsmccRequest(int requestId, @RequestOption int option, Uri uri) {
super(requestType, requestId, option);
mUri = uri;
}
diff --git a/media/java/android/media/tv/DsmccResponse.java b/media/java/android/media/tv/DsmccResponse.java
index 3bdfb956c889..e43d31adaed5 100644
--- a/media/java/android/media/tv/DsmccResponse.java
+++ b/media/java/android/media/tv/DsmccResponse.java
@@ -22,8 +22,9 @@ import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
/** @hide */
-public class DsmccResponse extends BroadcastInfoResponse implements Parcelable {
- public static final int responseType = BroadcastInfoType.DSMCC;
+public final class DsmccResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int responseType =
+ TvInputManager.BROADCAST_INFO_TYPE_DSMCC;
public static final @NonNull Parcelable.Creator<DsmccResponse> CREATOR =
new Parcelable.Creator<DsmccResponse>() {
@@ -39,30 +40,30 @@ public class DsmccResponse extends BroadcastInfoResponse implements Parcelable {
}
};
- private final ParcelFileDescriptor mFile;
+ private final ParcelFileDescriptor mFileDescriptor;
public static DsmccResponse createFromParcelBody(Parcel in) {
return new DsmccResponse(in);
}
- public DsmccResponse(int requestId, int sequence, int responseResult,
+ public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
ParcelFileDescriptor file) {
super(responseType, requestId, sequence, responseResult);
- mFile = file;
+ mFileDescriptor = file;
}
protected DsmccResponse(Parcel source) {
super(responseType, source);
- mFile = source.readFileDescriptor();
+ mFileDescriptor = source.readFileDescriptor();
}
public ParcelFileDescriptor getFile() {
- return mFile;
+ return mFileDescriptor;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- mFile.writeToParcel(dest, flags);
+ mFileDescriptor.writeToParcel(dest, flags);
}
}
diff --git a/media/java/android/media/tv/PesRequest.java b/media/java/android/media/tv/PesRequest.java
index 0e444b8f1d5a..7dedb65374d8 100644
--- a/media/java/android/media/tv/PesRequest.java
+++ b/media/java/android/media/tv/PesRequest.java
@@ -21,8 +21,9 @@ import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class PesRequest extends BroadcastInfoRequest implements Parcelable {
- public static final int requestType = BroadcastInfoType.PES;
+public final class PesRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int requestType =
+ TvInputManager.BROADCAST_INFO_TYPE_PES;
public static final @NonNull Parcelable.Creator<PesRequest> CREATOR =
new Parcelable.Creator<PesRequest>() {
@@ -45,7 +46,7 @@ public class PesRequest extends BroadcastInfoRequest implements Parcelable {
return new PesRequest(in);
}
- public PesRequest(int requestId, int option, int tsPid, int streamId) {
+ public PesRequest(int requestId, @RequestOption int option, int tsPid, int streamId) {
super(requestType, requestId, option);
mTsPid = tsPid;
mStreamId = streamId;
diff --git a/media/java/android/media/tv/PesResponse.java b/media/java/android/media/tv/PesResponse.java
index d46e6fcf1240..a657f911b2ab 100644
--- a/media/java/android/media/tv/PesResponse.java
+++ b/media/java/android/media/tv/PesResponse.java
@@ -21,8 +21,9 @@ import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class PesResponse extends BroadcastInfoResponse implements Parcelable {
- public static final int responseType = BroadcastInfoType.PES;
+public final class PesResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int responseType =
+ TvInputManager.BROADCAST_INFO_TYPE_PES;
public static final @NonNull Parcelable.Creator<PesResponse> CREATOR =
new Parcelable.Creator<PesResponse>() {
@@ -44,7 +45,8 @@ public class PesResponse extends BroadcastInfoResponse implements Parcelable {
return new PesResponse(in);
}
- public PesResponse(int requestId, int sequence, int responseResult, String sharedFilterToken) {
+ public PesResponse(int requestId, int sequence, @ResponseResult int responseResult,
+ String sharedFilterToken) {
super(responseType, requestId, sequence, responseResult);
mSharedFilterToken = sharedFilterToken;
}
diff --git a/media/java/android/media/tv/SectionRequest.java b/media/java/android/media/tv/SectionRequest.java
index 3e8e90901b71..533c5093395d 100644
--- a/media/java/android/media/tv/SectionRequest.java
+++ b/media/java/android/media/tv/SectionRequest.java
@@ -21,8 +21,9 @@ import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class SectionRequest extends BroadcastInfoRequest implements Parcelable {
- public static final int requestType = BroadcastInfoType.SECTION;
+public final class SectionRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int requestType =
+ TvInputManager.BROADCAST_INFO_TYPE_SECTION;
public static final @NonNull Parcelable.Creator<SectionRequest> CREATOR =
new Parcelable.Creator<SectionRequest>() {
@@ -40,13 +41,14 @@ public class SectionRequest extends BroadcastInfoRequest implements Parcelable {
private final int mTsPid;
private final int mTableId;
- private final int mVersion;
+ private final Integer mVersion;
public static SectionRequest createFromParcelBody(Parcel in) {
return new SectionRequest(in);
}
- public SectionRequest(int requestId, int option, int tsPid, int tableId, int version) {
+ public SectionRequest(int requestId, @RequestOption int option, int tsPid, int tableId,
+ Integer version) {
super(requestType, requestId, option);
mTsPid = tsPid;
mTableId = tableId;
@@ -57,7 +59,7 @@ public class SectionRequest extends BroadcastInfoRequest implements Parcelable {
super(requestType, source);
mTsPid = source.readInt();
mTableId = source.readInt();
- mVersion = source.readInt();
+ mVersion = (Integer) source.readValue(Integer.class.getClassLoader());
}
public int getTsPid() {
@@ -68,7 +70,7 @@ public class SectionRequest extends BroadcastInfoRequest implements Parcelable {
return mTableId;
}
- public int getVersion() {
+ public Integer getVersion() {
return mVersion;
}
@@ -77,6 +79,6 @@ public class SectionRequest extends BroadcastInfoRequest implements Parcelable {
super.writeToParcel(dest, flags);
dest.writeInt(mTsPid);
dest.writeInt(mTableId);
- dest.writeInt(mVersion);
+ dest.writeValue(mVersion);
}
}
diff --git a/media/java/android/media/tv/SectionResponse.java b/media/java/android/media/tv/SectionResponse.java
index 1c8f96560a59..d3fa3c042f63 100644
--- a/media/java/android/media/tv/SectionResponse.java
+++ b/media/java/android/media/tv/SectionResponse.java
@@ -22,8 +22,9 @@ import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class SectionResponse extends BroadcastInfoResponse implements Parcelable {
- public static final int responseType = BroadcastInfoType.SECTION;
+public final class SectionResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int responseType =
+ TvInputManager.BROADCAST_INFO_TYPE_SECTION;
public static final @NonNull Parcelable.Creator<SectionResponse> CREATOR =
new Parcelable.Creator<SectionResponse>() {
@@ -47,8 +48,8 @@ public class SectionResponse extends BroadcastInfoResponse implements Parcelable
return new SectionResponse(in);
}
- public SectionResponse(int requestId, int sequence, int responseResult, int sessionId,
- int version, Bundle sessionData) {
+ public SectionResponse(int requestId, int sequence, @ResponseResult int responseResult,
+ int sessionId, int version, Bundle sessionData) {
super(responseType, requestId, sequence, responseResult);
mSessionId = sessionId;
mVersion = version;
diff --git a/media/java/android/media/tv/StreamEventRequest.java b/media/java/android/media/tv/StreamEventRequest.java
index 09399c298722..84a5beee3dad 100644
--- a/media/java/android/media/tv/StreamEventRequest.java
+++ b/media/java/android/media/tv/StreamEventRequest.java
@@ -22,8 +22,9 @@ import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class StreamEventRequest extends BroadcastInfoRequest implements Parcelable {
- public static final int requestType = BroadcastInfoType.STREAM_EVENT;
+public final class StreamEventRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int requestType =
+ TvInputManager.BROADCAST_INFO_STREAM_EVENT;
public static final @NonNull Parcelable.Creator<StreamEventRequest> CREATOR =
new Parcelable.Creator<StreamEventRequest>() {
@@ -46,7 +47,8 @@ public class StreamEventRequest extends BroadcastInfoRequest implements Parcelab
return new StreamEventRequest(in);
}
- public StreamEventRequest(int requestId, int option, Uri targetUri, String eventName) {
+ public StreamEventRequest(int requestId, @RequestOption int option, Uri targetUri,
+ String eventName) {
super(requestType, requestId, option);
this.mTargetUri = targetUri;
this.mEventName = eventName;
diff --git a/media/java/android/media/tv/StreamEventResponse.java b/media/java/android/media/tv/StreamEventResponse.java
index 027b73513a26..fd7580107c71 100644
--- a/media/java/android/media/tv/StreamEventResponse.java
+++ b/media/java/android/media/tv/StreamEventResponse.java
@@ -21,8 +21,9 @@ import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class StreamEventResponse extends BroadcastInfoResponse implements Parcelable {
- public static final int responseType = BroadcastInfoType.STREAM_EVENT;
+public final class StreamEventResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int responseType =
+ TvInputManager.BROADCAST_INFO_STREAM_EVENT;
public static final @NonNull Parcelable.Creator<StreamEventResponse> CREATOR =
new Parcelable.Creator<StreamEventResponse>() {
@@ -47,8 +48,8 @@ public class StreamEventResponse extends BroadcastInfoResponse implements Parcel
return new StreamEventResponse(in);
}
- public StreamEventResponse(int requestId, int sequence, int responseResult, String name,
- String text, String data, String status) {
+ public StreamEventResponse(int requestId, int sequence, @ResponseResult int responseResult,
+ String name, String text, String data, String status) {
super(responseType, requestId, sequence, responseResult);
mName = name;
mText = text;
diff --git a/media/java/android/media/tv/TableRequest.java b/media/java/android/media/tv/TableRequest.java
index 54322158ff60..389536dd151f 100644
--- a/media/java/android/media/tv/TableRequest.java
+++ b/media/java/android/media/tv/TableRequest.java
@@ -16,17 +16,25 @@
package android.media.tv;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/** @hide */
-public class TableRequest extends BroadcastInfoRequest implements Parcelable {
- public static final int requestType = BroadcastInfoType.TABLE;
+public final class TableRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int requestType =
+ TvInputManager.BROADCAST_INFO_TYPE_TABLE;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({TABLE_NAME_PAT, TABLE_NAME_PMT})
+ public @interface TableName {}
- // todo: change const declaration to intdef
- public static final int PAT = 1;
- public static final int PMT = 2;
+ public static final int TABLE_NAME_PAT = 0;
+ public static final int TABLE_NAME_PMT = 1;
public static final @NonNull Parcelable.Creator<TableRequest> CREATOR =
new Parcelable.Creator<TableRequest>() {
@@ -43,14 +51,15 @@ public class TableRequest extends BroadcastInfoRequest implements Parcelable {
};
private final int mTableId;
- private final int mTableName;
+ private final @TableName int mTableName;
private final int mVersion;
public static TableRequest createFromParcelBody(Parcel in) {
return new TableRequest(in);
}
- public TableRequest(int requestId, int option, int tableId, int tableName, int version) {
+ public TableRequest(int requestId, @RequestOption int option, int tableId,
+ @TableName int tableName, int version) {
super(requestType, requestId, option);
mTableId = tableId;
mTableName = tableName;
@@ -68,7 +77,7 @@ public class TableRequest extends BroadcastInfoRequest implements Parcelable {
return mTableId;
}
- public int getTableName() {
+ public @TableName int getTableName() {
return mTableName;
}
diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java
index a6d3e3996825..912cbce81e93 100644
--- a/media/java/android/media/tv/TableResponse.java
+++ b/media/java/android/media/tv/TableResponse.java
@@ -22,8 +22,9 @@ import android.os.Parcelable;
import android.net.Uri;
/** @hide */
-public class TableResponse extends BroadcastInfoResponse implements Parcelable {
- public static final int responseType = BroadcastInfoType.TABLE;
+public final class TableResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int responseType =
+ TvInputManager.BROADCAST_INFO_TYPE_TABLE;
public static final @NonNull Parcelable.Creator<TableResponse> CREATOR =
new Parcelable.Creator<TableResponse>() {
@@ -47,8 +48,8 @@ public class TableResponse extends BroadcastInfoResponse implements Parcelable {
return new TableResponse(in);
}
- public TableResponse(int requestId, int sequence, int responseResult, Uri tableUri,
- int version, int size) {
+ public TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
+ Uri tableUri, int version, int size) {
super(responseType, requestId, sequence, responseResult);
mTableUri = tableUri;
mVersion = version;
diff --git a/media/java/android/media/tv/TsRequest.java b/media/java/android/media/tv/TsRequest.java
index 141f3ac91828..99350c991629 100644
--- a/media/java/android/media/tv/TsRequest.java
+++ b/media/java/android/media/tv/TsRequest.java
@@ -21,8 +21,9 @@ import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class TsRequest extends BroadcastInfoRequest implements Parcelable {
- public static final int requestType = BroadcastInfoType.TS;
+public final class TsRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int requestType =
+ TvInputManager.BROADCAST_INFO_TYPE_TS;
public static final @NonNull Parcelable.Creator<TsRequest> CREATOR =
new Parcelable.Creator<TsRequest>() {
@@ -44,7 +45,7 @@ public class TsRequest extends BroadcastInfoRequest implements Parcelable {
return new TsRequest(in);
}
- public TsRequest(int requestId, int option, int tsPid) {
+ public TsRequest(int requestId, @RequestOption int option, int tsPid) {
super(requestType, requestId, option);
mTsPid = tsPid;
}
diff --git a/media/java/android/media/tv/TsResponse.java b/media/java/android/media/tv/TsResponse.java
index e30ff54af4aa..c5ec53ab9432 100644
--- a/media/java/android/media/tv/TsResponse.java
+++ b/media/java/android/media/tv/TsResponse.java
@@ -21,8 +21,9 @@ import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class TsResponse extends BroadcastInfoResponse implements Parcelable {
- public static final int responseType = BroadcastInfoType.TS;
+public final class TsResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final @TvInputManager.BroadcastInfoType int responseType =
+ TvInputManager.BROADCAST_INFO_TYPE_TS;
public static final @NonNull Parcelable.Creator<TsResponse> CREATOR =
new Parcelable.Creator<TsResponse>() {
@@ -44,7 +45,8 @@ public class TsResponse extends BroadcastInfoResponse implements Parcelable {
return new TsResponse(in);
}
- public TsResponse(int requestId, int sequence, int responseResult, String sharedFilterToken) {
+ public TsResponse(int requestId, int sequence, @ResponseResult int responseResult,
+ String sharedFilterToken) {
super(responseType, requestId, sequence, responseResult);
this.mSharedFilterToken = sharedFilterToken;
}
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index a0f6fb9577c3..9147c123c6f3 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -2720,6 +2720,42 @@ public final class TvContract {
*/
public static final String COLUMN_GLOBAL_CONTENT_ID = "global_content_id";
+ /**
+ * The flag indicating whether this TV program is scrambled or not.
+ *
+ * <p>Use the same coding for scrambled in the underlying broadcast standard
+ * if {@code free_ca_mode} in EIT is defined there (e.g. ETSI EN 300 468).
+ *
+ * <p>Type: INTEGER (boolean)
+ */
+ public static final String COLUMN_SCRAMBLED = "scrambled";
+
+ /**
+ * The comma-separated series IDs of this TV program for episodic TV shows.
+ *
+ * <p>This is used to indicate the series IDs.
+ * Programs in the same series share a series ID.
+ * Use this instead of {@link #COLUMN_SERIES_ID} if more than one series IDs
+ * are assigned to the TV program.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_MULTI_SERIES_ID = "multi_series_id";
+
+ /**
+ * The internal ID used by individual TV input services.
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+
private Programs() {}
/** Canonical genres for TV programs. */
@@ -3052,6 +3088,32 @@ public final class TvContract {
public static final String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS =
"recording_expire_time_utc_millis";
+ /**
+ * The comma-separated series IDs of this TV program for episodic TV shows.
+ *
+ * <p>This is used to indicate the series IDs.
+ * Programs in the same series share a series ID.
+ * Use this instead of {@link #COLUMN_SERIES_ID} if more than one series IDs
+ * are assigned to the TV program.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_MULTI_SERIES_ID = "multi_series_id";
+
+ /**
+ * The internal ID used by individual TV input services.
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+
private RecordedPrograms() {}
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index b655a615794e..aeb3e3cfa6c2 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -357,6 +357,28 @@ public final class TvInputManager {
*/
public static final int INPUT_STATE_DISCONNECTED = 2;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({BROADCAST_INFO_TYPE_TS, BROADCAST_INFO_TYPE_TABLE, BROADCAST_INFO_TYPE_SECTION,
+ BROADCAST_INFO_TYPE_PES, BROADCAST_INFO_STREAM_EVENT, BROADCAST_INFO_TYPE_DSMCC,
+ BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION})
+ public @interface BroadcastInfoType {}
+
+ /** @hide */
+ public static final int BROADCAST_INFO_TYPE_TS = 1;
+ /** @hide */
+ public static final int BROADCAST_INFO_TYPE_TABLE = 2;
+ /** @hide */
+ public static final int BROADCAST_INFO_TYPE_SECTION = 3;
+ /** @hide */
+ public static final int BROADCAST_INFO_TYPE_PES = 4;
+ /** @hide */
+ public static final int BROADCAST_INFO_STREAM_EVENT = 5;
+ /** @hide */
+ public static final int BROADCAST_INFO_TYPE_DSMCC = 6;
+ /** @hide */
+ public static final int BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION = 7;
+
/**
* An unknown state of the client pid gets from the TvInputManager. Client gets this value when
* query through {@link getClientPid(String sessionId)} fails.
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
index 30ef50342c11..a5f23315956d 100644
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
@@ -15,9 +15,9 @@
*/
package android.media.tv.interactive;
-import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoRequest;
+import android.os.Bundle;
import android.view.InputChannel;
/**
@@ -31,4 +31,5 @@ oneway interface ITvIAppClient {
void onLayoutSurface(int left, int top, int right, int bottom, int seq);
void onBroadcastInfoRequest(in BroadcastInfoRequest request, int seq);
void onSessionStateChanged(int state, int seq);
-} \ No newline at end of file
+ void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
+}
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
index ff8af88fa5fa..66f5fc1d54aa 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
@@ -19,6 +19,7 @@ package android.media.tv.interactive;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.interactive.ITvIAppSession;
import android.media.tv.BroadcastInfoRequest;
+import android.os.Bundle;
/**
* Helper interface for ITvIAppSession to allow TvIAppService to notify the system service when
@@ -30,4 +31,5 @@ oneway interface ITvIAppSessionCallback {
void onLayoutSurface(int left, int top, int right, int bottom);
void onBroadcastInfoRequest(in BroadcastInfoRequest request);
void onSessionStateChanged(int state);
-} \ No newline at end of file
+ void onCommandRequest(in String cmdType, in Bundle parameters);
+}
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index a232e3181e81..9390d8db08fb 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -244,6 +244,19 @@ public final class TvIAppManager {
}
@Override
+ public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType,
+ Bundle parameters, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postCommandRequest(cmdType, parameters);
+ }
+ }
+
+ @Override
public void onSessionStateChanged(int state, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -1046,6 +1059,16 @@ public final class TvIAppManager {
});
}
+ void postCommandRequest(final @TvIAppService.IAppServiceCommandType String cmdType,
+ final Bundle parameters) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onCommandRequest(mSession, cmdType, parameters);
+ }
+ });
+ }
+
void postSessionStateChanged(int state) {
mHandler.post(new Runnable() {
@Override
@@ -1093,6 +1116,17 @@ public final class TvIAppManager {
}
/**
+ * This is called when {@link TvIAppService.Session#requestCommand} is called.
+ *
+ * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param cmdType type of the command.
+ * @param parameters parameters of the command.
+ */
+ public void onCommandRequest(Session session,
+ @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+ }
+
+ /**
* This is called when {@link TvIAppService.Session#notifySessionStateChanged} is called.
*
* @param session A {@link TvIAppManager.Session} associated with this callback.
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index 1e11bafa6c3b..6475f903f257 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -19,6 +19,7 @@ package android.media.tv.interactive;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Service;
@@ -53,6 +54,8 @@ import android.widget.FrameLayout;
import com.android.internal.os.SomeArgs;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -83,6 +86,28 @@ public abstract class TvIAppService extends Service {
*/
public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "IAPP_SERVICE_COMMAND_TYPE_", value = {
+ IAPP_SERVICE_COMMAND_TYPE_TUNE,
+ IAPP_SERVICE_COMMAND_TYPE_TUNENEXT,
+ IAPP_SERVICE_COMMAND_TYPE_TUNEPREV,
+ IAPP_SERVICE_COMMAND_TYPE_STOP,
+ IAPP_SERVICE_COMMAND_TYPE_SETSTREAMVOLUME
+ })
+ public @interface IAppServiceCommandType {}
+
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE = "Tune";
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_TUNENEXT = "TuneNext";
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_TUNEPREV = "TunePrevious";
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_STOP = "Stop";
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_SETSTREAMVOLUME = "setStreamVolume";
+
private final Handler mServiceHandler = new ServiceHandler();
private final RemoteCallbackList<ITvIAppServiceCallback> mCallbacks =
new RemoteCallbackList<>();
@@ -443,6 +468,31 @@ public abstract class TvIAppService extends Service {
});
}
+ /**
+ * requests a specific command to be processed by the related TV input.
+ * @param cmdType type of the specific command
+ * @param parameters parameters of the specific command
+ */
+ public void requestCommand(@IAppServiceCommandType String cmdType, Bundle parameters) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "requestCommand (cmdType=" + cmdType + ", parameters="
+ + parameters.toString() + ")");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onCommandRequest(cmdType, parameters);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestCommand", e);
+ }
+ }
+ });
+ }
+
void startIApp() {
onStartIApp();
}
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java
index 803198162d30..efbe9e3bcf4b 100644
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ b/media/java/android/media/tv/interactive/TvIAppView.java
@@ -26,6 +26,7 @@ import android.media.tv.TvInputManager;
import android.media.tv.TvView;
import android.media.tv.interactive.TvIAppManager.Session;
import android.media.tv.interactive.TvIAppManager.SessionCallback;
+import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
@@ -53,6 +54,7 @@ public class TvIAppView extends ViewGroup {
private final Handler mHandler = new Handler();
private Session mSession;
private MySessionCallback mSessionCallback;
+ private TvIAppCallback mCallback;
private SurfaceView mSurfaceView;
private Surface mSurface;
@@ -123,6 +125,16 @@ public class TvIAppView extends ViewGroup {
mTvIAppManager = (TvIAppManager) getContext().getSystemService("tv_interactive_app");
}
+ /**
+ * Sets the callback to be invoked when an event is dispatched to this TvIAppView.
+ *
+ * @param callback The callback to receive events. A value of {@code null} removes the existing
+ * callback.
+ */
+ public void setCallback(@Nullable TvIAppCallback callback) {
+ mCallback = callback;
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -322,6 +334,23 @@ public class TvIAppView extends ViewGroup {
return UNSET_TVVIEW_SUCCESS;
}
+ /**
+ * Callback used to receive various status updates on the {@link TvIAppView}.
+ */
+ public abstract static class TvIAppCallback {
+
+ /**
+ * This is called when a command is requested to be processed by the related TV input.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @param cmdType type of the command
+ * @param parameters parameters of the command
+ */
+ public void onCommandRequest(String iAppServiceId,
+ @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+ }
+ }
+
private class MySessionCallback extends SessionCallback {
final String mIAppServiceId;
int mType;
@@ -395,5 +424,21 @@ public class TvIAppView extends ViewGroup {
mUseRequestedSurfaceLayout = true;
requestLayout();
}
+
+ @Override
+ public void onCommandRequest(Session session,
+ @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+ if (DEBUG) {
+ Log.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
+ + parameters.toString() + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onCommandRequest - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onCommandRequest(mIAppServiceId, cmdType, parameters);
+ }
+ }
}
}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index 6f3ab032a75c..11e699981e80 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -281,11 +281,11 @@ public class DvrPlayback implements AutoCloseable {
/**
* Sets the file pointer offset of the file descriptor.
*
- * @param pos the offset position, measured in bytes from the beginning of the file.
- * @return the new offset position.
+ * @param position the offset position, measured in bytes from the beginning of the file.
+ * @return the new offset position. On error, {@code -1} is returned.
*/
@BytesLong
- public long seek(@BytesLong long pos) {
- return nativeSeek(pos);
+ public long seek(@BytesLong long position) {
+ return nativeSeek(position);
}
}
diff --git a/media/java/android/media/tv/tuner/filter/DownloadSettings.java b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
index e2cfd7c173a2..a686bf4f76b0 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadSettings.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
@@ -47,9 +47,12 @@ public class DownloadSettings extends Settings {
/**
* Gets whether download ID is used.
*
+ * If it's set to false, HAL will begin to send data before it knows downloadId and document
+ * structures.
+ *
* <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
- * return {@code false}.
- * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ * return {@code false}. Use {@link TunerVersionChecker#getTunerVersion()} to get the version
+ * information.
*/
public boolean useDownloadId() { return mUseDownloadId; }
@@ -78,6 +81,9 @@ public class DownloadSettings extends Settings {
/**
* Sets whether download ID is used or not.
*
+ * If it's set to false, HAL will begin to send data before it knows downloadId and document
+ * structures.
+ *
* <p>This configuration is only supported in Tuner 2.0 or higher version. Unsupported
* version will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the
* version information.
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index f0f576e90d91..5d71a13bf701 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -159,7 +159,8 @@ public class Filter implements AutoCloseable {
public @interface Status {}
/**
- * The status of a filter that the data in the filter buffer is ready to be read.
+ * The status of a filter that the data in the filter buffer is ready to be read. It can also be
+ * used to know the STC (System Time Clock) ready status if it's PCR filter.
*/
public static final int STATUS_DATA_READY = DemuxFilterStatus.DATA_READY;
/**
diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
index 58a81d99ff99..c8bb178253af 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -55,7 +55,7 @@ public class MmtpRecordEvent extends FilterEvent {
}
/**
- * Gets data size in bytes of filtered data.
+ * Gets the record data offset from the beginning of the record buffer.
*/
@BytesLong
public long getDataLength() {
diff --git a/media/java/android/media/tv/tuner/filter/SectionEvent.java b/media/java/android/media/tv/tuner/filter/SectionEvent.java
index 182bb9463bd6..04b65c471457 100644
--- a/media/java/android/media/tv/tuner/filter/SectionEvent.java
+++ b/media/java/android/media/tv/tuner/filter/SectionEvent.java
@@ -16,6 +16,7 @@
package android.media.tv.tuner.filter;
+import android.annotation.BytesLong;
import android.annotation.SystemApi;
/**
@@ -69,5 +70,11 @@ public class SectionEvent extends FilterEvent {
return (int) getDataLengthLong();
}
- public long getDataLengthLong() { return mDataLength; }
+ /**
+ * Gets data size in bytes of filtered data.
+ */
+ @BytesLong
+ public long getDataLengthLong() {
+ return mDataLength;
+ }
}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
index 58e22c907639..94fda30fecb1 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettings.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -45,8 +45,19 @@ public abstract class SectionSettings extends Settings {
public boolean isCrcEnabled() {
return mCrcEnabled;
}
+
/**
- * Returns whether the filter repeats the data with the same version.
+ * Returns whether the filter repeats the data.
+ *
+ * If {@code false}, for {@link SectionSettingsWithTableInfo}, HAL filters out all sections
+ * based on {@link SectionSettingsWithTableInfo} TableId and Version, and stops filtering data.
+ * For {@link SectionSettingsWithSectionBits}, HAL filters out the first section which matches
+ * the {@link SectionSettingsWithSectionBits} configuration, and stops filtering data.
+ *
+ * If {@code true}, for {@link SectionSettingsWithTableInfo}, HAL filters out all sections based
+ * on {@link SectionSettingsWithTableInfo} TableId and Version, and repeats. For
+ * {@link SectionSettingsWithSectionBits}, HAL filters out sections which match the
+ * {@link SectionSettingsWithSectionBits} configuration, and repeats.
*/
public boolean isRepeat() {
return mIsRepeat;
@@ -83,8 +94,20 @@ public abstract class SectionSettings extends Settings {
mCrcEnabled = crcEnabled;
return self();
}
+
/**
- * Sets whether the filter repeats the data with the same version.
+ * Sets whether the filter repeats the data.
+ *
+ * If {@code false}, for {@link SectionSettingsWithTableInfo}, HAL filters out all sections
+ * based on {@link SectionSettingsWithTableInfo} TableId and Version, and stops filtering
+ * data. For {@link SectionSettingsWithSectionBits}, HAL filters out the first section which
+ * matches the {@link SectionSettingsWithSectionBits} configuration, and stops filtering
+ * data.
+ *
+ * If {@code true}, for {@link SectionSettingsWithTableInfo}, HAL filters out all sections
+ * based on {@link SectionSettingsWithTableInfo} TableId and Version, and repeats. For
+ * {@link SectionSettingsWithSectionBits}, HAL filters out sections which match the
+ * {@link SectionSettingsWithSectionBits} configuration, and repeats.
*/
@NonNull
public T setRepeat(boolean isRepeat) {
diff --git a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
index bf2c000cb6e8..12ea22246356 100644
--- a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
@@ -71,7 +71,7 @@ public class TsRecordEvent extends FilterEvent {
}
/**
- * Gets data size in bytes of filtered data.
+ * Gets the record data offset from the beginning of the record buffer.
*/
@BytesLong
public long getDataLength() {
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 582e4f54e2c3..92eafec6a13a 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -53,7 +53,7 @@ public class FrontendStatus {
FRONTEND_STATUS_TYPE_MODULATIONS_EXT, FRONTEND_STATUS_TYPE_ROLL_OFF,
FRONTEND_STATUS_TYPE_IS_MISO_ENABLED, FRONTEND_STATUS_TYPE_IS_LINEAR,
FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED, FRONTEND_STATUS_TYPE_ISDBT_MODE,
- FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG, FRONTEND_STATUS_TYPE_STREAM_ID_LIST})
+ FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG, FRONTEND_STATUS_TYPE_STREAM_IDS})
@Retention(RetentionPolicy.SOURCE)
public @interface FrontendStatusType {}
@@ -255,9 +255,9 @@ public class FrontendStatus {
android.hardware.tv.tuner.FrontendStatusType.ISDBT_PARTIAL_RECEPTION_FLAG;
/**
- * Stream ID list included in a transponder.
+ * Stream IDs included in a transponder.
*/
- public static final int FRONTEND_STATUS_TYPE_STREAM_ID_LIST =
+ public static final int FRONTEND_STATUS_TYPE_STREAM_IDS =
android.hardware.tv.tuner.FrontendStatusType.STREAM_ID_LIST;
/** @hide */
@@ -645,6 +645,8 @@ public class FrontendStatus {
}
/**
* Gets the current Automatic Gain Control value which is normalized from 0 to 255.
+ *
+ * Larger AGC values indicate it is applying more gain.
*/
public int getAgc() {
if (mAgc == null) {
@@ -663,6 +665,10 @@ public class FrontendStatus {
}
/**
* Gets the current Error information by layer.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
public boolean[] getLayerErrors() {
@@ -736,6 +742,10 @@ public class FrontendStatus {
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
public int[] getBers() {
@@ -752,6 +762,10 @@ public class FrontendStatus {
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
@FrontendSettings.InnerFec
@@ -849,6 +863,10 @@ public class FrontendStatus {
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
@FrontendInterleaveMode
@@ -867,6 +885,10 @@ public class FrontendStatus {
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
@IntRange(from = 0, to = 0xff)
@@ -900,6 +922,10 @@ public class FrontendStatus {
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
@FrontendModulation
@@ -1008,19 +1034,19 @@ public class FrontendStatus {
}
/**
- * Gets stream id list included in a transponder.
+ * Gets stream ids included in a transponder.
*
* <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version or if HAL
- * doesn't return stream id list status will throw IllegalStateException. Use
+ * doesn't return stream ids will throw IllegalStateException. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
*/
@SuppressLint("ArrayReturn")
@NonNull
- public int[] getStreamIdList() {
+ public int[] getStreamIds() {
TunerVersionChecker.checkHigherOrEqualVersionTo(
- TunerVersionChecker.TUNER_VERSION_2_0, "stream id list status");
+ TunerVersionChecker.TUNER_VERSION_2_0, "stream ids status");
if (mStreamIds == null) {
- throw new IllegalStateException("stream id list status is empty");
+ throw new IllegalStateException("stream ids are empty");
}
return mStreamIds;
}
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index f61bd525beb4..cb35edb15c62 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -28,7 +28,12 @@ import android.annotation.SystemApi;
@SystemApi
public interface ScanCallback {
- /** Scan locked the signal. */
+ /**
+ * Scan locked the signal.
+ *
+ * It can also be notified after signal is locked if the signal attributes transmission
+ * parameter of the signal is changed (e.g., Modulation).
+ */
void onLocked();
/** Scan stopped. */
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 4de2c23e62b8..c06c81ed03ec 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -330,6 +330,6 @@ void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled
iq->finishEvent(e, handled != 0);
}
-AInputQueue* AInputQueue_fromJava(jobject inputQueue) {
- return android::android_view_InputQueue_getNativePtr(inputQueue);
+AInputQueue* AInputQueue_fromJava(JNIEnv* env, jobject inputQueue) {
+ return android::android_view_InputQueue_getNativePtr(env, inputQueue);
}
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index 3faed5525946..aec8f89f20e7 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Metgeseltoestel-bestuurder"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Laat &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toe om jou &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te bestuur"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string>
<string name="chooser_title" msgid="2262294130493605839">"Kies \'n <xliff:g id="PROFILE_NAME">%1$s</xliff:g> om deur &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; bestuur te word"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Laat &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toe om jou &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te bestuur"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Hierdie program is nodig om jou <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string>
<string name="consent_no" msgid="2640796915611404382">"Moenie toelaat nie"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index 99466d7d2e1a..235912410580 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"አጃቢ የመሣሪያ አስተዳዳሪ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> - &lt;strong&gt;&lt;/strong&gt; እንዲያስተዳደር ይፍቀዱ"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string>
<string name="chooser_title" msgid="2262294130493605839">"በ&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; የሚተዳደር <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ይምረጡ"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> - &lt;strong&gt;&lt;/strong&gt; እንዲያስተዳደር ይፍቀዱ"</string>
- <string name="profile_summary" msgid="2059360676631420073">"የእርስዎን <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ለማስተዳደር ይህ መተግበሪያ ያስፈልጋል <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string>
<string name="consent_no" msgid="2640796915611404382">"አትፍቀድ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index c3f1e73d3ad4..5fc3a4fc8633 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"تطبيق \"مدير الجهاز المصاحب\""</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‏السماح للتطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بإدارة &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ساعة"</string>
<string name="chooser_title" msgid="2262294130493605839">"‏اختَر <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ليديره تطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ساعة"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"‏السماح للتطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بإدارة &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"هذا التطبيق مطلوب لإدارة <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"السماح"</string>
<string name="consent_no" msgid="2640796915611404382">"عدم السماح"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index 2a2bc25c7881..743d725621e5 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"কম্পেনিয়ন ডিভাইচ মেনেজাৰ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; পৰিচালনা কৰিবলৈ দিয়ক"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ী"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;এ পৰিচালনা কৰিব লগা এটা <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বাছনি কৰক"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ী"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; পৰিচালনা কৰিবলৈ দিয়ক"</string>
- <string name="profile_summary" msgid="2059360676631420073">"আপোনাৰ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> পৰিচালনা কৰিবলৈ এই এপ্‌টোৰ আৱশ্যক। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিয়ক"</string>
<string name="consent_no" msgid="2640796915611404382">"অনুমতি নিদিব"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index 2ec13c587166..ca32052d4758 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Kompanyon Cihaz Meneceri"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı idarə etməsinə icazə verin"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; tərəfindən idarə ediləcək <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı idarə etməsinə icazə verin"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Bu tətbiq <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizi idarə etmək üçün lazımdır. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string>
<string name="consent_no" msgid="2640796915611404382">"İcazə verməyin"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index d687b05896c6..d919e6734030 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Menadžer pridruženog uređaja"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
<string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
<string name="consent_no" msgid="2640796915611404382">"Ne dozvoli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index 2236052f5545..919d7290866f 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Менеджар спадарожнай прылады"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string>
<string name="chooser_title" msgid="2262294130493605839">"Выберыце прыладу (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), якой будзе кіраваць праграма &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Гэта праграма неабходная для кіравання профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string>
<string name="consent_no" msgid="2640796915611404382">"Не дазваляць"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index 996ca9062f80..1e2aa4e8214c 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Разрешаване на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството ви &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
<string name="chooser_title" msgid="2262294130493605839">"Изберете устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), което да се управлява от &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Разрешаване на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството ви &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Това приложение е необходимо за управление на <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string>
<string name="consent_no" msgid="2640796915611404382">"Забраняване"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index 16d25ce57870..3b537b6e48bf 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"আপনার &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; -কে অনুমতি দিন"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ি"</string>
<string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> বেছে নিন যেটি &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করবে"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ি"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"আপনার &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; -কে অনুমতি দিন"</string>
- <string name="profile_summary" msgid="2059360676631420073">"আপনার <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ম্যানেজ করতে এই অ্যাপটি প্রয়োজন। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string>
<string name="consent_no" msgid="2640796915611404382">"অনুমতি দেবেন না"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 10f753c1cf45..b010626ab395 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Prateći upravitelj uređaja"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
<string name="chooser_title" msgid="2262294130493605839">"Odaberite uređaj <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
<string name="consent_no" msgid="2640796915611404382">"Nemoj dozvoliti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index e55a033cf800..efd801e59bde 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gestor de dispositius complementaris"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permet que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni el teu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string>
<string name="chooser_title" msgid="2262294130493605839">"Tria un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> perquè el gestioni &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permet que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni el teu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Aquesta aplicació es necessita per gestionar el teu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permet"</string>
<string name="consent_no" msgid="2640796915611404382">"No permetis"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 48fbda10cfed..bd57213ca02e 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Správce doprovodných zařízení"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Povolit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovat &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
<string name="chooser_title" msgid="2262294130493605839">"Vyberte zařízení <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, které chcete spravovat pomocí aplikace &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Povolit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovat &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Tato aplikace je nutná pro správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Povolit"</string>
<string name="consent_no" msgid="2640796915611404382">"Nepovolovat"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 446c301747df..742845310dea 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Medfølgende enhedsadministrator"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Tillad at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; kan administrere: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ur"</string>
<string name="chooser_title" msgid="2262294130493605839">"Vælg den enhed (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), som skal administreres af &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ur"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Tillad at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; kan administrere: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Du skal bruge denne app for at administrere dit <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Tillad"</string>
<string name="consent_no" msgid="2640796915611404382">"Tillad ikke"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index 33d831d41aec..4c431401c490 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Begleitgerät-Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; erlauben, dein Gerät &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; zu verwalten"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Gerät (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) auswählen, das von &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; verwaltet werden soll"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; erlauben, dein Gerät &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; zu verwalten"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Diese App wird zur Verwaltung des Profils „<xliff:g id="PROFILE_NAME">%1$s</xliff:g>“ benötigt. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Zulassen"</string>
<string name="consent_no" msgid="2640796915611404382">"Nicht zulassen"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index 7a78c0669825..07a4fda587a8 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Διαχείριση συνοδευτικής εφαρμογής"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Επιτρέψτε στην εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; να διαχειρίζεται τη συσκευή &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string>
<string name="chooser_title" msgid="2262294130493605839">"Επιλέξτε ένα προφίλ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> για διαχείριση από την εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Επιτρέψτε στην εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; να διαχειρίζεται τη συσκευή &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Αυτή η εφαρμογή είναι απαραίτητη για τη διαχείριση του προφίλ σας <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string>
<string name="consent_no" msgid="2640796915611404382">"Να μην επιτρέπεται"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
index a6ebe658d622..1756d22c129f 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -17,11 +17,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="summary_watch" product="default" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="summary_watch" product="tablet" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="title_app_streaming" msgid="4459136600249308574">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream applications?"</string>
+ <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this phone when connected."</string>
+ <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this tablet when connected."</string>
+ <string name="summary_app_streaming" product="device" msgid="7614171699434639963">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this device when connected."</string>
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
<string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
index a6ebe658d622..1756d22c129f 100644
--- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
@@ -17,11 +17,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="summary_watch" product="default" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="summary_watch" product="tablet" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="title_app_streaming" msgid="4459136600249308574">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream applications?"</string>
+ <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this phone when connected."</string>
+ <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this tablet when connected."</string>
+ <string name="summary_app_streaming" product="device" msgid="7614171699434639963">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this device when connected."</string>
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
<string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
index a6ebe658d622..1756d22c129f 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -17,11 +17,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="summary_watch" product="default" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="summary_watch" product="tablet" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="title_app_streaming" msgid="4459136600249308574">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream applications?"</string>
+ <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this phone when connected."</string>
+ <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this tablet when connected."</string>
+ <string name="summary_app_streaming" product="device" msgid="7614171699434639963">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this device when connected."</string>
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
<string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
index a6ebe658d622..1756d22c129f 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -17,11 +17,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="summary_watch" product="default" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="summary_watch" product="tablet" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="title_app_streaming" msgid="4459136600249308574">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream applications?"</string>
+ <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this phone when connected."</string>
+ <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this tablet when connected."</string>
+ <string name="summary_app_streaming" product="device" msgid="7614171699434639963">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this device when connected."</string>
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
<string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
index 6cc56a4f44e7..efda04ec0d3a 100644
--- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
@@ -17,11 +17,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎Companion Device Manager‎‏‎‎‏‎"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‎‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to manage your &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎watch‎‏‎‎‏‎"</string>
<string name="chooser_title" msgid="2262294130493605839">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎Choose a ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to be managed by &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
+ <string name="summary_watch" product="default" msgid="7113724443198337683">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions.‎‏‎‎‏‎"</string>
+ <string name="summary_watch" product="tablet" msgid="7113724443198337683">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions.‎‏‎‎‏‎"</string>
+ <string name="title_app_streaming" msgid="4459136600249308574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‏‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to stream applications?‎‏‎‎‏‎"</string>
+ <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎Let &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to provide &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; remote access to access to applications installed on this phone when connected.‎‏‎‎‏‎"</string>
+ <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‎Let &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to provide &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; remote access to access to applications installed on this tablet when connected.‎‏‎‎‏‎"</string>
+ <string name="summary_app_streaming" product="device" msgid="7614171699434639963">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‎‏‏‎Let &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to provide &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; remote access to access to applications installed on this device when connected.‎‏‎‎‏‎"</string>
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎device‎‏‎‎‏‎"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎watch‎‏‎‎‏‎"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‎‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to manage your &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
- <string name="profile_summary" msgid="2059360676631420073">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎This app is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎Allow‎‏‎‎‏‎"</string>
<string name="consent_no" msgid="2640796915611404382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‏‏‎‎Don’t allow‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index dc1315916159..9bbc1b8a8831 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Administrador de dispositivo complementario"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permite que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administre tu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
<string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para que &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; lo administre"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permite que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administre tu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Esta app es necesaria para administrar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
<string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index 7e37c1d33522..daece565a14e 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos complementario"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestione tu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
<string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para gestionarlo con &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestione tu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Se necesita esta aplicación para gestionar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
<string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index 399556de789b..ffaa0c0709fb 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Kaasseadme haldur"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Lubage rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hallata teie seadet &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"käekell"</string>
<string name="chooser_title" msgid="2262294130493605839">"Valige seade <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, mida haldab rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"seade"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"käekell"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Lubage rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hallata teie seadet &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Seda rakendust on vaja teie profiili <xliff:g id="PROFILE_NAME">%1$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Luba"</string>
<string name="consent_no" msgid="2640796915611404382">"Ära luba"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 764505e52959..5bf6677de7eb 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gailu osagarriaren kudeatzailea"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Eman &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kudeatzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
<string name="chooser_title" msgid="2262294130493605839">"Aukeratu &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Eman &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kudeatzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Aplikazioa <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kudeatzeko beharrezkoa da. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string>
<string name="consent_no" msgid="2640796915611404382">"Ez eman baimenik"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index 07d04aa1ecc9..1ede28cebfc6 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"مدیر دستگاه مرتبط"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‏مجاز کردن &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت کردن &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string>
<string name="chooser_title" msgid="2262294130493605839">"‏انتخاب <xliff:g id="PROFILE_NAME">%1$s</xliff:g> برای مدیریت کردن با &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>‏&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"‏مجاز کردن &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت کردن &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"این برنامه برای مدیریت <xliff:g id="PROFILE_NAME">%1$s</xliff:g> شما لازم است. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"مجاز بودن"</string>
<string name="consent_no" msgid="2640796915611404382">"مجاز نبودن"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index 528d16c2e425..ac948dfdc059 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Salli, että &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; voi hallinnoida tätä laitettasi: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"kello"</string>
<string name="chooser_title" msgid="2262294130493605839">"Valitse <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, jota &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; hallinnoi"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"laite"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"kello"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Salli, että &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; voi hallinnoida tätä laitettasi: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Profiilin (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) ylläpitoon tarvitaan tätä sovellusta. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Salli"</string>
<string name="consent_no" msgid="2640796915611404382">"Älä salli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index 1dcd3375c1cf..19f97f0c5887 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareil compagnon"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
<string name="chooser_title" msgid="2262294130493605839">"Choisissez un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré par &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Cette application est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
<string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index ba2fc8ea9424..8a7ae1ad807f 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareils associés"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
<string name="chooser_title" msgid="2262294130493605839">"Sélectionner le/la <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré(e) par &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Cette appli est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
<string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index 5f9a8d786739..052c2073068e 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Xestor de dispositivos complementarios"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; xestione o teu dispositivo (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;)"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string>
<string name="chooser_title" msgid="2262294130493605839">"Escolle un perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) para que o xestione a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; xestione o teu dispositivo (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;)"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Esta aplicación é necesaria para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
<string name="consent_no" msgid="2640796915611404382">"Non permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 71cf012f24b3..279de1622612 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"કમ્પેનિયન ડિવાઇસ મેનેજર"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"તમારા &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ને મેનેજ કરવા માટે &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને મંજૂર કરો"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; દ્વારા મેનેજ કરવા માટે કોઈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> પસંદ કરો"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"તમારા &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ને મેનેજ કરવા માટે &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને મંજૂર કરો"</string>
- <string name="profile_summary" msgid="2059360676631420073">"તમારી <xliff:g id="PROFILE_NAME">%1$s</xliff:g> મેનેજ કરવા માટે આ ઍપ જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string>
<string name="consent_no" msgid="2640796915611404382">"મંજૂરી આપશો નહીં"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index d4dd1cb4f1b1..77048290af65 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"सहयोगी डिवाइस मैनेजर"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को, अपनी &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; मैनेज करने की अनुमति दें"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string>
<string name="chooser_title" msgid="2262294130493605839">"कोई <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चुनें, ताकि उसे &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; की मदद से प्रबंधित किया जा सके"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को, अपनी &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; मैनेज करने की अनुमति दें"</string>
- <string name="profile_summary" msgid="2059360676631420073">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> को मैनेज करने के लिए, यह ऐप्लिकेशन ज़रूरी है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string>
<string name="consent_no" msgid="2640796915611404382">"अनुमति न दें"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
index 87c5ae2670e9..e7db2badbdc1 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Dopustite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"satom"</string>
<string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"satom"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Dopustite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ta je aplikacija potrebna za upravljanje vašim profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string>
<string name="consent_no" msgid="2640796915611404382">"Nemoj dopustiti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index c7ceb384ed16..56f02a5808ab 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Társeszközök kezelője"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; engedélyezése a(z) &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kezelésére"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"óra"</string>
<string name="chooser_title" msgid="2262294130493605839">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; alkalmazással kezelni kívánt <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiválasztása"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"óra"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; engedélyezése a(z) &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kezelésére"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Szükség van erre az alkalmazásra a következő kezeléséhez: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string>
<string name="consent_no" msgid="2640796915611404382">"Tiltás"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index 26f7990d7b66..cf22fbcf12a2 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Թույլատրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին կառավարել ձեր &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; սարքը"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string>
<string name="chooser_title" msgid="2262294130493605839">"Ընտրեք <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ը, որը պետք է կառավարվի &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; հավելվածի կողմից"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Թույլատրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին կառավարել ձեր &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; սարքը"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Այս հավելվածն անհրաժեշտ է <xliff:g id="PROFILE_NAME">%1$s</xliff:g> սարքը կամ պրոֆիլը կառավարելու համար։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string>
<string name="consent_no" msgid="2640796915611404382">"Չթույլատրել"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index b0618d409527..41f1d096a01a 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Pengelola Perangkat Pendamping"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengelola &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk dikelola oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengelola &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Aplikasi ini diperlukan untuk mengelola <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string>
<string name="consent_no" msgid="2640796915611404382">"Jangan izinkan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index b7d7c6abf749..5376912c6479 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Stjórnun fylgdartækja"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; stjórn á: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"úr"</string>
<string name="chooser_title" msgid="2262294130493605839">"Velja <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sem &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; á að stjórna"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"úr"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; stjórn á: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Þetta forrit er nauðsynlegt til að hafa umsjón með <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string>
<string name="consent_no" msgid="2640796915611404382">"Ekki leyfa"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
index ce003e7d7645..af9e8ca38a10 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gestione dispositivi companion"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Consenti all\'app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di gestire &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"orologio"</string>
<string name="chooser_title" msgid="2262294130493605839">"Scegli un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> che sia gestito da &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"orologio"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Consenti all\'app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di gestire &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Questa app è necessaria per gestire il tuo <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Consenti"</string>
<string name="consent_no" msgid="2640796915611404382">"Non consentire"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 54c523c92f4b..68ca9d94e1fb 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ניהול מכשיר מותאם"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‏אישור לאפליקציה &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לנהל את &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"שעון"</string>
<string name="chooser_title" msgid="2262294130493605839">"‏בחירת <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"שעון"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"‏אישור לאפליקציה &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לנהל את &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"האפליקציה הזו נחוצה כדי לנהל את ה<xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string>
<string name="consent_no" msgid="2640796915611404382">"אין אישור"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index f92fafe54fde..c10a1e1f5226 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"コンパニオン デバイス マネージャ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; に &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理を許可する"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理対象となる<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の選択"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; に &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理を許可する"</string>
- <string name="profile_summary" msgid="2059360676631420073">"このアプリは <xliff:g id="PROFILE_NAME">%1$s</xliff:g> の管理に必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"許可"</string>
<string name="consent_no" msgid="2640796915611404382">"許可しない"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
index 34efdd2223ac..637248170fe7 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"კომპანიონი მოწყობილობების მენეჯერი"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ნება დართეთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/strong&gt;, რომ მართოს თქვენი &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"საათი"</string>
<string name="chooser_title" msgid="2262294130493605839">"აირჩიეთ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, რომელიც უნდა მართოს &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;-მა"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"საათი"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"ნება დართეთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/strong&gt;, რომ მართოს თქვენი &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ეს აპი საჭიროა თქვენი <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-ს სამართავად. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"დაშვება"</string>
<string name="consent_no" msgid="2640796915611404382">"არ დაიშვას"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index 3c7f697b984f..6ac9c04732a5 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; құрылғысын басқаруға рұқсат беру"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"сағат"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; арқылы басқарылатын <xliff:g id="PROFILE_NAME">%1$s</xliff:g> құрылғысын таңдаңыз"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"сағат"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; құрылғысын басқаруға рұқсат беру"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Бұл қолданба <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профиліңізді басқару үшін қажет. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string>
<string name="consent_no" msgid="2640796915611404382">"Рұқсат бермеу"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
index 74ccd84956ea..db2634f9ec79 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"កម្មវិធី​គ្រប់​គ្រង​ឧបករណ៍ដៃគូ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; គ្រប់គ្រង &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; របស់អ្នក"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string>
<string name="chooser_title" msgid="2262294130493605839">"ជ្រើសរើស <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ដើម្បីឱ្យស្ថិតក្រោម​ការគ្រប់គ្រងរបស់ &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; គ្រប់គ្រង &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; របស់អ្នក"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ត្រូវការកម្មវិធីនេះ ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%1$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string>
<string name="consent_no" msgid="2640796915611404382">"កុំអនុញ្ញាត"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index 2a82d1fd6bac..e6413dac75d2 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ಕಂಪ್ಯಾನಿಯನ್ ಸಾಧನ ನಿರ್ವಾಹಕರು"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ನಿಮ್ಮ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ಅನುಮತಿಸಿ"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ಮೂಲಕ ನಿರ್ವಹಿಸಬೇಕಾದ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"ನಿಮ್ಮ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ಅನುಮತಿಸಿ"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. ಅನ್ನು ನಿರ್ವಹಿಸಲು ಈ ಆ್ಯಪ್‌ನ ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string>
<string name="consent_no" msgid="2640796915611404382">"ಅನುಮತಿಸಬೇಡಿ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 6ca01bff5364..02459d51cfbc 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"부속 기기 관리자"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; 기기를 관리하도록 허용"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"시계"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;에서 관리할 <xliff:g id="PROFILE_NAME">%1$s</xliff:g>을(를) 선택"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"기기"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"시계"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; 기기를 관리하도록 허용"</string>
- <string name="profile_summary" msgid="2059360676631420073">"이 앱은 <xliff:g id="PROFILE_NAME">%1$s</xliff:g> 프로필을 관리하는 데 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"허용"</string>
<string name="consent_no" msgid="2640796915611404382">"허용 안함"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index 18d38a832e0b..ea4230add677 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; түзмөгүңүздү башкарууга уруксат бериңиз"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"саат"</string>
<string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; тарабынан башкарылсын"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"саат"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; түзмөгүңүздү башкарууга уруксат бериңиз"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Бул колдонмо <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профилиңизди башкаруу үчүн керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Уруксат берүү"</string>
<string name="consent_no" msgid="2640796915611404382">"Уруксат берилбесин"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
index a1eb71342446..b6c6289cd029 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ຕົວຈັດການອຸປະກອນປະກອບ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ອະນຸຍາດໃຫ້ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ຈັດການ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ຂອງທ່ານໄດ້"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string>
<string name="chooser_title" msgid="2262294130493605839">"ເລືອກ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ເພື່ອໃຫ້ຖືກຈັດການໂດຍ &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"ອະນຸຍາດໃຫ້ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ຈັດການ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ຂອງທ່ານໄດ້"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ຕ້ອງໃຊ້ແອັບນີ້ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ອະນຸຍາດ"</string>
<string name="consent_no" msgid="2640796915611404382">"ບໍ່ອະນຸຍາດ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
index 65f371d3d312..e5ff48025338 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Leisti &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tvarkyti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"laikrodį"</string>
<string name="chooser_title" msgid="2262294130493605839">"Jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, kurį valdys &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; (pasirinkite)"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"laikrodį"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Leisti &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tvarkyti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ši programa reikalinga norint tvarkyti jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Leisti"</string>
<string name="consent_no" msgid="2640796915611404382">"Neleisti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index b18bfe4a4243..d5212401e27e 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Palīgierīču pārzinis"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Atļauja lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pārvaldīt ierīci &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string>
<string name="chooser_title" msgid="2262294130493605839">"Profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) izvēle, ko pārvaldīt lietotnē &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Atļauja lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pārvaldīt ierīci &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Šī lietotne ir nepieciešama jūsu profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) pārvaldībai. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string>
<string name="consent_no" msgid="2640796915611404382">"Neatļaut"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index 9d745c413b39..e6131e607f3b 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -17,11 +17,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управува со вашиот &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
<string name="chooser_title" msgid="2262294130493605839">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> со којшто ќе управува &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="summary_watch" product="default" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> ќе може да остварува интеракција со известувањата и да пристапува до дозволите за телефонот, SMS, контактите и календарот."</string>
+ <string name="summary_watch" product="tablet" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> ќе може да остварува интеракција со известувањата и да пристапува до дозволите за телефонот, SMS, контактите и календарот."</string>
+ <string name="title_app_streaming" msgid="4459136600249308574">"Да се дозволи &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да стримува апликации?"</string>
+ <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да обезбеди далечински пристап на &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; за да пристапува до апликации инсталирани на телефонов кога ќе се поврзе."</string>
+ <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да обезбеди далечински пристап на &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; за да пристапува до апликации инсталирани на таблетов кога ќе се поврзе."</string>
+ <string name="summary_app_streaming" product="device" msgid="7614171699434639963">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да обезбеди далечински пристап на &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; за да пристапува до апликации инсталирани на уредов кога ќе се поврзе."</string>
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"уред"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управува со вашиот &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Апликацијава е потребна за управување со вашиот <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
<string name="consent_no" msgid="2640796915611404382">"Не дозволувај"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index 28c88da9e4af..639909a3f260 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"കമ്പാനിയൻ ഉപകരണ മാനേജർ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"നിങ്ങളുടെ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; മാനേജ് ചെയ്യാൻ, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കുക"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ഉപയോഗിച്ച് മാനേജ് ചെയ്യുന്നതിന് ഒരു <xliff:g id="PROFILE_NAME">%1$s</xliff:g> തിരഞ്ഞെടുക്കുക"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"നിങ്ങളുടെ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; മാനേജ് ചെയ്യാൻ, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കുക"</string>
- <string name="profile_summary" msgid="2059360676631420073">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> മാനേജ് ചെയ്യാൻ ഈ ആപ്പ് ആവശ്യമാണ്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string>
<string name="consent_no" msgid="2640796915611404382">"അനുവദിക്കരുത്"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index 11e61d910ee4..adbe62d041c5 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-г удирдахын тулд &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г зөвшөөрнө үү"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"цаг"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;-н удирдах<xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г сонгоно уу"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"цаг"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-г удирдахын тулд &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г зөвшөөрнө үү"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Энэ апп таны <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г удирдахад шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string>
<string name="consent_no" msgid="2640796915611404382">"Бүү зөвшөөр"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index c73ed283c768..fce058399702 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"सहयोगी डिव्हाइस व्यवस्थापक"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"तुमचे &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला अनुमती द्या"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"वॉच"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; द्वारे व्यवस्थापित करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"वॉच"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"तुमचे &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला अनुमती द्या"</string>
- <string name="profile_summary" msgid="2059360676631420073">"तुमची <xliff:g id="PROFILE_NAME">%1$s</xliff:g> प्रोफाइल व्यवस्थापित करण्यासाठी हे ॲप आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string>
<string name="consent_no" msgid="2640796915611404382">"अनुमती देऊ नका"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index d2aebb45f49e..5c4ec781132f 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Pengurus Peranti Rakan"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; anda"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string>
<string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk diurus oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; anda"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Apl ini diperlukan untuk mengurus <xliff:g id="PROFILE_NAME">%1$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Benarkan"</string>
<string name="consent_no" msgid="2640796915611404382">"Jangan benarkan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index 45c9d8b240fa..f3e572e0e542 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"တွဲဖက်ကိရိယာ မန်နေဂျာ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"သင်၏ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ကို စီမံခန့်ခွဲရန် &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို ခွင့်ပြုပါ"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; က စီမံခန့်ခွဲရန် <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို ရွေးချယ်ပါ"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"သင်၏ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ကို စီမံခန့်ခွဲရန် &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို ခွင့်ပြုပါ"</string>
- <string name="profile_summary" msgid="2059360676631420073">"သင်၏ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို စီမံခန့်ခွဲရန် ဤအက်ပ်ကိုလိုအပ်သည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string>
<string name="consent_no" msgid="2640796915611404382">"ခွင့်မပြုပါ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index af1ffe94cd36..d3eb7e595c8d 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Tillat at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administrerer &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"klokke"</string>
<string name="chooser_title" msgid="2262294130493605839">"Velg <xliff:g id="PROFILE_NAME">%1$s</xliff:g> som skal administreres av &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"klokke"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Tillat at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administrerer &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Denne appen kreves for å administrere <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Tillat"</string>
<string name="consent_no" msgid="2640796915611404382">"Ikke tillat"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index b29f94ce1b05..9bcf69b07fcc 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"सहयोगी डिभाइसको प्रबन्धक"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"आफ्नो &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; लाई &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; व्यवस्थापन गर्ने अनुमति दिनुहोस्"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"घडी"</string>
<string name="chooser_title" msgid="2262294130493605839">"आफूले &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; प्रयोग गरी व्यवस्थापन गर्न चाहेको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चयन गर्नुहोस्"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"घडी"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"आफ्नो &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; लाई &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; व्यवस्थापन गर्ने अनुमति दिनुहोस्"</string>
- <string name="profile_summary" msgid="2059360676631420073">"तपाईंको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> व्यवस्थापन गर्न यो एपलाई अनुमति दिनु पर्ने हुन्छ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"अनुमति दिनुहोस्"</string>
<string name="consent_no" msgid="2640796915611404382">"अनुमति नदिनुहोस्"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index a56fb9a62ce7..9ee09db08385 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toestaan je &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te beheren"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"horloge"</string>
<string name="chooser_title" msgid="2262294130493605839">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om te beheren met &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"horloge"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toestaan je &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te beheren"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Deze app is vereist om je <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string>
<string name="consent_no" msgid="2640796915611404382">"Niet toestaan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index 8e43213a9e43..e08ec28eeab5 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ସହଯୋଗୀ ଡିଭାଇସ୍ ପରିଚାଳକ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ଆପଣଙ୍କ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ଦ୍ୱାରା ପରିଚାଳିତ ହେବା ପାଇଁ ଏକ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ବାଛନ୍ତୁ"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"ଆପଣଙ୍କ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ ଏହି ଆପ୍ ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="consent_no" msgid="2640796915611404382">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 54f4f8c42302..e317a464f72a 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ਸੰਬੰਧੀ ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਕ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਜਾਣ ਲਈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਚੁਣੋ"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ਤੁਹਾਡੇ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਇਹ ਐਪ ਲੋੜੀਂਦੀ ਹੈ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ਇਜਾਜ਼ਤ ਦਿਓ"</string>
<string name="consent_no" msgid="2640796915611404382">"ਇਜਾਜ਼ਤ ਨਾ ਦਿਓ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index a989baa4e36c..6cb7cc68d72e 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Menedżer urządzeń towarzyszących"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Zezwól na zarządzanie urządzeniem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; przez aplikację &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string>
<string name="chooser_title" msgid="2262294130493605839">"Wybierz profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, którym ma zarządzać aplikacja &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Zezwól na zarządzanie urządzeniem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; przez aplikację &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacja jest niezbędna do zarządzania profilem <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string>
<string name="consent_no" msgid="2640796915611404382">"Nie zezwalaj"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index d2724c0a8127..4306286937d7 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
<string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
index 2f5a53b2e2a7..8f05d49c7cbb 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos associados"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permita que a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça a gestão do seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerido pela app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permita que a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça a gestão do seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Esta app é necessária para gerir o seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
<string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index d2724c0a8127..4306286937d7 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
<string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index 4df74de2a441..43a4de7b1cb5 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Manager de dispozitiv Companion"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permiteți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să vă gestioneze dispozitivul &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ceas"</string>
<string name="chooser_title" msgid="2262294130493605839">"Alegeți un profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pe care să îl gestioneze &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ceas"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permiteți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să vă gestioneze dispozitivul &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Această aplicație este necesară pentru a gestiona <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string>
<string name="consent_no" msgid="2640796915611404382">"Nu permiteți"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index ea372d51658b..6d5c0de9e012 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Управление подключенными устройствами"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять этим устройством: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"часы"</string>
<string name="chooser_title" msgid="2262294130493605839">"Выберите устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), которым будет управлять приложение &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"часы"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять этим устройством: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Это приложение необходимо для управления вашим профилем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string>
<string name="consent_no" msgid="2640796915611404382">"Запретить"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index a5c2c8842830..b4e28d8cdb65 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"සහායක උපාංග කළමනාකරු"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඔබගේ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; කළමනාකරණය කිරීමට ඉඩ දෙන්න"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; මගින් කළමනාකරණය කරනු ලැබීමට <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ක් තෝරන්න"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඔබගේ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; කළමනාකරණය කිරීමට ඉඩ දෙන්න"</string>
- <string name="profile_summary" msgid="2059360676631420073">"මෙම යෙදුමට ඔබගේ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> කළමනාකරණය කිරීමට අවශ්‍යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string>
<string name="consent_no" msgid="2640796915611404382">"ඉඩ නොදෙන්න"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index a9bf77f809e5..4f86f08e83f9 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Správca sprievodných zariadení"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Povoliť aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovať zariadenie &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
<string name="chooser_title" msgid="2262294130493605839">"Vyberte profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ktorý bude spravovať aplikácia &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Povoliť aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovať zariadenie &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Táto aplikácia sa vyžaduje na správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string>
<string name="consent_no" msgid="2640796915611404382">"Nepovoliť"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index 4eb8f5029fcd..a54af21d3586 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Upravitelj spremljevalnih naprav"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dovolite upravljanje naprave &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ura"</string>
<string name="chooser_title" msgid="2262294130493605839">"Izbira naprave <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ki jo bo upravljala aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ura"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dovolite upravljanje naprave &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacija je potrebna za upravljanje profila »<xliff:g id="PROFILE_NAME">%1$s</xliff:g>«. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Dovoli"</string>
<string name="consent_no" msgid="2640796915611404382">"Ne dovoli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 34357b478a17..d3f97df1bff0 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Menaxheri i pajisjes shoqëruese"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Lejo që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të menaxhojë pajisjen tënde &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string>
<string name="chooser_title" msgid="2262294130493605839">"Zgjidh një profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> që do të menaxhohet nga &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Lejo që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të menaxhojë pajisjen tënde &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ky aplikacion nevojitet për të menaxhuar profilin tënd <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Lejo"</string>
<string name="consent_no" msgid="2640796915611404382">"Mos lejo"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index 37af18534712..db8f291d6874 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Менаџер придруженог уређаја"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дозволите апликацији &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управља уређајем &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"сат"</string>
<string name="chooser_title" msgid="2262294130493605839">"Одаберите профил <xliff:g id="PROFILE_NAME">%1$s</xliff:g> којим ће управљати апликација &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"сат"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Дозволите апликацији &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управља уређајем &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ова апликација је потребна за управљање профилом <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
<string name="consent_no" msgid="2640796915611404382">"Не дозволи"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml
index f78fadf8d864..733b2f799d29 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Tillåt att &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hanterar din &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"klocka"</string>
<string name="chooser_title" msgid="2262294130493605839">"Välj en <xliff:g id="PROFILE_NAME">%1$s</xliff:g> för hantering av &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"klocka"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Tillåt att &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hanterar din &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Appen behövs för att hantera <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Tillåt"</string>
<string name="consent_no" msgid="2640796915611404382">"Tillåt inte"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 495c44111290..02db2569f9eb 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Kidhibiti cha Vifaa Visaidizi"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; idhibiti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yako"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"saa"</string>
<string name="chooser_title" msgid="2262294130493605839">"Chagua <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ili idhibitiwe na &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"saa"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; idhibiti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yako"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Programu hii inahitajika ili udhibiti wasifu wako wa <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string>
<string name="consent_no" msgid="2640796915611404382">"Usiruhusu"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 20845bd69562..3e998c8a1983 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"கம்பேனியன் சாதன நிர்வாகி"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"உங்கள் &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ஐ நிர்வகிக்க &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதியுங்கள்"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ஆப்ஸ் நிர்வகிக்கக்கூடிய <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ஐத் தேர்ந்தெடுங்கள்"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"உங்கள் &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ஐ நிர்வகிக்க &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதியுங்கள்"</string>
- <string name="profile_summary" msgid="2059360676631420073">"உங்கள் <xliff:g id="PROFILE_NAME">%1$s</xliff:g> சுயவிவரத்தை நிர்வகிக்க இந்த ஆப்ஸ் தேவைப்படுகிறது. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string>
<string name="consent_no" msgid="2640796915611404382">"அனுமதிக்க வேண்டாம்"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index c855cf2a8501..8c5312695c89 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"సహచర పరికర మేనేజర్"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"మీ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ను మేనేజ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ను అనుమతించండి;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ద్వారా మేనేజ్ చేయబడటానికి ఒక <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను ఎంచుకోండి"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"మీ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ను మేనేజ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ను అనుమతించండి;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"మీ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను మేనేజ్ చేయడానికి ఈ యాప్ అవసరం. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"అనుమతించు"</string>
<string name="consent_no" msgid="2640796915611404382">"అనుమతించవద్దు"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index d78ada26cd8a..504731e9cb05 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; จัดการ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ของคุณ"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string>
<string name="chooser_title" msgid="2262294130493605839">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะให้มีการจัดการโดย &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; จัดการ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ของคุณ"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ต้องใช้แอปนี้ในการจัดการ<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ของคุณ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string>
<string name="consent_no" msgid="2640796915611404382">"ไม่อนุญาต"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index 03165b43ab0f..30d5e5719f1d 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Kasamang Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na pamahalaan ang iyong &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"relo"</string>
<string name="chooser_title" msgid="2262294130493605839">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para pamahalaan ng &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"relo"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na pamahalaan ang iyong &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Kinakailangan ang app na ito para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Payagan"</string>
<string name="consent_no" msgid="2640796915611404382">"Huwag payagan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index b2c1cf2fa832..1b1d71d36fb7 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulaması &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı yönetebilsin mi?"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"saat"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; tarafından yönetilecek bir <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"saat"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulaması &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı yönetebilsin mi?"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Bu uygulama, <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizin yönetilmesi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string>
<string name="consent_no" msgid="2640796915611404382">"İzin verme"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index 61b78e9a0b18..149841a84ead 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Диспетчер супутніх пристроїв"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дозволити додатку &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; керувати вашим пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"годинник"</string>
<string name="chooser_title" msgid="2262294130493605839">"Виберіть <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, яким керуватиме додаток &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"годинник"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Дозволити додатку &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; керувати вашим пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Цей додаток потрібен, щоб керувати профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string>
<string name="consent_no" msgid="2640796915611404382">"Не дозволяти"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index ee7992109a18..b467550324dc 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ساتھی آلہ مینیجر"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‏اپنے &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; کا نظم کرنے کے لیے ‎&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو اجازت دیں"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string>
<string name="chooser_title" msgid="2262294130493605839">"‏&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; کے ذریعے نظم کئے جانے کیلئے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کو منتخب کریں"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"‏اپنے &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; کا نظم کرنے کے لیے ‎&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو اجازت دیں"</string>
- <string name="profile_summary" msgid="2059360676631420073">"اس ایپ کو آپ کے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کا نظم کرنے کی ضرورت ہے۔ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string>
<string name="consent_no" msgid="2640796915611404382">"اجازت نہ دیں"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index 7221b6d24893..8505ca93c442 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; qurilmasini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga ruxsat bering"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"soat"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; boshqaradigan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qurilmasini tanlang"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"soat"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; qurilmasini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga ruxsat bering"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Bu ilova <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilini boshqarish uchun kerak. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string>
<string name="consent_no" msgid="2640796915611404382">"Ruxsat berilmasin"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index 2819e1df75ba..5dec271a9f71 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Cho phép &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; quản lý &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; của bạn"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"đồng hồ"</string>
<string name="chooser_title" msgid="2262294130493605839">"Chọn một <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sẽ do &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; quản lý"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"đồng hồ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Cho phép &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; quản lý &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; của bạn"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Cần có ứng dụng này để quản lý <xliff:g id="PROFILE_NAME">%1$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string>
<string name="consent_no" msgid="2640796915611404382">"Không cho phép"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index 1440c401673e..f41406790299 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"配套设备管理器"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"允许&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理您的&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"手表"</string>
<string name="chooser_title" msgid="2262294130493605839">"选择要由&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"设备"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"手表"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"允许&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理您的&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"需要使用此应用,才能管理您的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"允许"</string>
<string name="consent_no" msgid="2640796915611404382">"不允许"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index e3f1eb1249f1..37f4bd9aa099 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"隨附裝置管理工具"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"允許 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 管理您的&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
<string name="chooser_title" msgid="2262294130493605839">"選擇由 &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; 管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"允許 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 管理您的&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"必須使用此應用程式,才能管理<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"允許"</string>
<string name="consent_no" msgid="2640796915611404382">"不允許"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index 9f4041dd0aff..76c837927c57 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理你的「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
<string name="chooser_title" msgid="2262294130493605839">"選擇要讓「<xliff:g id="APP_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理你的「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"需使用這個應用程式,才能管理「<xliff:g id="PROFILE_NAME">%1$s</xliff:g>」。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"允許"</string>
<string name="consent_no" msgid="2640796915611404382">"不允許"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index dc933ae21599..fdfda007fea4 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Isiphathi sedivayisi esihambisanayo"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Vumela i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuthi iphathe i-&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yakho"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"buka"</string>
<string name="chooser_title" msgid="2262294130493605839">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ezophathwa yi-&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"buka"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Vumela i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuthi iphathe i-&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yakho"</string>
- <string name="profile_summary" msgid="2059360676631420073">"I-app iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Vumela"</string>
<string name="consent_no" msgid="2640796915611404382">"Ungavumeli"</string>
</resources>
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
index 931a55b27ddb..0bda923f2389 100644
--- a/packages/ConnectivityT/framework-t/Android.bp
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -114,6 +114,23 @@ filegroup {
],
}
+// Ethernet related libraries.
+
+filegroup {
+ name: "framework-connectivity-ethernet-sources",
+ srcs: [
+ "src/android/net/EthernetManager.java",
+ "src/android/net/EthernetNetworkSpecifier.java",
+ "src/android/net/IEthernetManager.aidl",
+ "src/android/net/IEthernetServiceListener.aidl",
+ "src/android/net/ITetheredInterfaceCallback.aidl",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
// Connectivity-T common libraries.
filegroup {
@@ -130,6 +147,7 @@ filegroup {
filegroup {
name: "framework-connectivity-tiramisu-sources",
srcs: [
+ ":framework-connectivity-ethernet-sources",
":framework-connectivity-ipsec-sources",
":framework-connectivity-netstats-sources",
":framework-connectivity-nsd-sources",
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index 8a6c85d54896..ca8330921620 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -45,8 +45,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.DataUnit;
@@ -135,15 +133,6 @@ public class NetworkStatsManager {
private int mFlags;
- /**
- * {@hide}
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public NetworkStatsManager(Context context) throws ServiceNotFoundException {
- this(context, INetworkStatsService.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)));
- }
-
/** @hide */
@VisibleForTesting
public NetworkStatsManager(Context context, INetworkStatsService service) {
diff --git a/core/java/android/net/EthernetManager.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
index 7cd63ef9cc5a..7cd63ef9cc5a 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
diff --git a/core/java/android/net/EthernetNetworkSpecifier.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java
index 62c576144221..62c576144221 100644
--- a/core/java/android/net/EthernetNetworkSpecifier.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java
diff --git a/core/java/android/net/IEthernetManager.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
index e058e5a70c71..e058e5a70c71 100644
--- a/core/java/android/net/IEthernetManager.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
diff --git a/core/java/android/net/IEthernetServiceListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetServiceListener.aidl
index 782fa19d9df7..782fa19d9df7 100644
--- a/core/java/android/net/IEthernetServiceListener.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetServiceListener.aidl
diff --git a/core/java/android/net/ITetheredInterfaceCallback.aidl b/packages/ConnectivityT/framework-t/src/android/net/ITetheredInterfaceCallback.aidl
index 14aa0237f24a..14aa0237f24a 100644
--- a/core/java/android/net/ITetheredInterfaceCallback.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/ITetheredInterfaceCallback.aidl
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
index 840af28b77e7..a84e7a9c6344 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
@@ -23,7 +23,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.HexDump;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -469,20 +468,12 @@ public final class IpSecAlgorithm implements Parcelable {
}
}
- // Because encryption keys are sensitive and userdebug builds are used by large user pools
- // such as beta testers, we only allow sensitive info such as keys on eng builds.
- private static boolean isUnsafeBuild() {
- return Build.IS_DEBUGGABLE && Build.IS_ENG;
- }
-
@Override
@NonNull
public String toString() {
return new StringBuilder()
.append("{mName=")
.append(mName)
- .append(", mKey=")
- .append(isUnsafeBuild() ? HexDump.toHexString(mKey) : "<hidden>")
.append(", mTruncLenBits=")
.append(mTruncLenBits)
.append("}")
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
index 837629911ccd..0b266b226c78 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
@@ -86,6 +86,7 @@ public final class IpSecManager {
*
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public static final int DIRECTION_FWD = 2;
/**
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
index c7ffc1933829..1986b83b12d8 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
@@ -16,7 +16,7 @@
package android.net;
-import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
+import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
import android.annotation.IntDef;
import android.annotation.NonNull;
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index 0d3b9ed4e3d4..8d1347e25bb6 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -30,7 +30,7 @@ import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_REMOVED;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
-import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
+import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
import android.os.Binder;
import android.service.NetworkStatsCollectionKeyProto;
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
index a875e1ad45a3..3eef4ee6f829 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
@@ -28,8 +28,8 @@ import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
-import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
import static com.android.internal.util.ArrayUtils.total;
+import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index 8b9c14df7dc8..d85291318fa4 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -24,6 +24,8 @@ import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkIdentity.OEM_NONE;
+import static android.net.NetworkIdentity.OEM_PAID;
+import static android.net.NetworkIdentity.OEM_PRIVATE;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
@@ -35,6 +37,7 @@ import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.ROAMING_YES;
import static android.net.wifi.WifiInfo.sanitizeSsid;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -44,15 +47,21 @@ import android.os.Parcelable;
import android.telephony.Annotation.NetworkType;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.ArraySet;
import com.android.internal.util.ArrayUtils;
import com.android.net.module.util.NetworkIdentityUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
/**
* Predicate used to match {@link NetworkIdentity}, usually when collecting
@@ -60,40 +69,88 @@ import java.util.Objects;
*
* @hide
*/
-public class NetworkTemplate implements Parcelable {
- private static final String TAG = "NetworkTemplate";
-
+// @SystemApi(client = MODULE_LIBRARIES)
+public final class NetworkTemplate implements Parcelable {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "MATCH_" }, value = {
+ MATCH_MOBILE,
+ MATCH_WIFI,
+ MATCH_ETHERNET,
+ MATCH_BLUETOOTH,
+ MATCH_CARRIER
+ })
+ public @interface TemplateMatchRule{}
+
+ /** Match rule to match cellular networks with given Subscriber Ids. */
public static final int MATCH_MOBILE = 1;
+ /** Match rule to match wifi networks. */
public static final int MATCH_WIFI = 4;
+ /** Match rule to match ethernet networks. */
public static final int MATCH_ETHERNET = 5;
+ /**
+ * Match rule to match all cellular networks.
+ *
+ * @hide
+ */
public static final int MATCH_MOBILE_WILDCARD = 6;
+ /**
+ * Match rule to match all wifi networks.
+ *
+ * @hide
+ */
public static final int MATCH_WIFI_WILDCARD = 7;
+ /** Match rule to match bluetooth networks. */
public static final int MATCH_BLUETOOTH = 8;
+ /**
+ * Match rule to match networks with {@link Connectivity#TYPE_PROXY} as the legacy network type.
+ *
+ * @hide
+ */
public static final int MATCH_PROXY = 9;
+ /**
+ * Match rule to match all networks with subscriberId inside the template. Some carriers
+ * may offer non-cellular networks like WiFi, which will be matched by this rule.
+ */
public static final int MATCH_CARRIER = 10;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "SUBSCRIBER_ID_MATCH_RULE_" }, value = {
+ SUBSCRIBER_ID_MATCH_RULE_EXACT,
+ SUBSCRIBER_ID_MATCH_RULE_ALL
+ })
+ public @interface SubscriberIdMatchRule{}
/**
* Value of the match rule of the subscriberId to match networks with specific subscriberId.
+ *
+ * @hide
*/
public static final int SUBSCRIBER_ID_MATCH_RULE_EXACT = 0;
/**
* Value of the match rule of the subscriberId to match networks with any subscriberId which
* includes null and non-null.
+ *
+ * @hide
*/
public static final int SUBSCRIBER_ID_MATCH_RULE_ALL = 1;
+ // TODO: Remove this and replace all callers with WIFI_NETWORK_KEY_ALL.
+ /** @hide */
+ public static final String WIFI_NETWORKID_ALL = null;
+
/**
- * Wi-Fi Network ID is never supposed to be null (if it is, it is a bug that
+ * Wi-Fi Network Key is never supposed to be null (if it is, it is a bug that
* should be fixed), so it's not possible to want to match null vs
- * non-null. Therefore it's fine to use null as a sentinel for Network ID.
+ * non-null. Therefore it's fine to use null as a sentinel for Wifi Network Key.
+ *
+ * @hide
*/
- public static final String WIFI_NETWORKID_ALL = null;
+ public static final String WIFI_NETWORK_KEY_ALL = WIFI_NETWORKID_ALL;
/**
* Include all network types when filtering. This is meant to merge in with the
* {@code TelephonyManager.NETWORK_TYPE_*} constants, and thus needs to stay in sync.
- *
- * @hide
*/
public static final int NETWORK_TYPE_ALL = -1;
/**
@@ -106,21 +163,37 @@ public class NetworkTemplate implements Parcelable {
*/
public static final int NETWORK_TYPE_5G_NSA = -2;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "OEM_MANAGED_" }, value = {
+ OEM_MANAGED_ALL,
+ OEM_MANAGED_NO,
+ OEM_MANAGED_YES,
+ OEM_MANAGED_PAID,
+ OEM_MANAGED_PRIVATE
+ })
+ public @interface OemManaged{}
+
/**
* Value to match both OEM managed and unmanaged networks (all networks).
- * @hide
*/
public static final int OEM_MANAGED_ALL = -1;
/**
* Value to match networks which are not OEM managed.
- * @hide
*/
public static final int OEM_MANAGED_NO = OEM_NONE;
/**
* Value to match any OEM managed network.
- * @hide
*/
public static final int OEM_MANAGED_YES = -2;
+ /**
+ * Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID}.
+ */
+ public static final int OEM_MANAGED_PAID = OEM_PAID;
+ /**
+ * Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE}.
+ */
+ public static final int OEM_MANAGED_PRIVATE = OEM_PRIVATE;
private static boolean isKnownMatchRule(final int rule) {
switch (rule) {
@@ -142,6 +215,8 @@ public class NetworkTemplate implements Parcelable {
/**
* Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
* the given IMSI.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public static NetworkTemplate buildTemplateMobileAll(String subscriberId) {
@@ -152,6 +227,8 @@ public class NetworkTemplate implements Parcelable {
* Template to match cellular networks with the given IMSI, {@code ratType} and
* {@code metered}. Use {@link #NETWORK_TYPE_ALL} to include all network types when
* filtering. See {@code TelephonyManager.NETWORK_TYPE_*}.
+ *
+ * @hide
*/
public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
@NetworkType int ratType, int metered) {
@@ -168,6 +245,8 @@ public class NetworkTemplate implements Parcelable {
/**
* Template to match metered {@link ConnectivityManager#TYPE_MOBILE} networks,
* regardless of IMSI.
+ *
+ * @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static NetworkTemplate buildTemplateMobileWildcard() {
@@ -177,6 +256,8 @@ public class NetworkTemplate implements Parcelable {
/**
* Template to match all metered {@link ConnectivityManager#TYPE_WIFI} networks,
* regardless of SSID.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public static NetworkTemplate buildTemplateWifiWildcard() {
@@ -185,6 +266,7 @@ public class NetworkTemplate implements Parcelable {
return new NetworkTemplate(MATCH_WIFI_WILDCARD, null, null);
}
+ /** @hide */
@Deprecated
@UnsupportedAppUsage
public static NetworkTemplate buildTemplateWifi() {
@@ -194,6 +276,8 @@ public class NetworkTemplate implements Parcelable {
/**
* Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
* given SSID.
+ *
+ * @hide
*/
public static NetworkTemplate buildTemplateWifi(@NonNull String networkId) {
Objects.requireNonNull(networkId);
@@ -208,7 +292,10 @@ public class NetworkTemplate implements Parcelable {
* Template to match all {@link ConnectivityManager#TYPE_WIFI} networks with the given SSID,
* and IMSI.
*
- * Call with {@link #WIFI_NETWORKID_ALL} for {@code networkId} to get result regardless of SSID.
+ * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code networkId} to get result regardless
+ * of SSID.
+ *
+ * @hide
*/
public static NetworkTemplate buildTemplateWifi(@Nullable String networkId,
@Nullable String subscriberId) {
@@ -221,6 +308,8 @@ public class NetworkTemplate implements Parcelable {
/**
* Template to combine all {@link ConnectivityManager#TYPE_ETHERNET} style
* networks together.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public static NetworkTemplate buildTemplateEthernet() {
@@ -230,6 +319,8 @@ public class NetworkTemplate implements Parcelable {
/**
* Template to combine all {@link ConnectivityManager#TYPE_BLUETOOTH} style
* networks together.
+ *
+ * @hide
*/
public static NetworkTemplate buildTemplateBluetooth() {
return new NetworkTemplate(MATCH_BLUETOOTH, null, null);
@@ -238,6 +329,8 @@ public class NetworkTemplate implements Parcelable {
/**
* Template to combine all {@link ConnectivityManager#TYPE_PROXY} style
* networks together.
+ *
+ * @hide
*/
public static NetworkTemplate buildTemplateProxy() {
return new NetworkTemplate(MATCH_PROXY, null, null);
@@ -245,6 +338,8 @@ public class NetworkTemplate implements Parcelable {
/**
* Template to match all metered carrier networks with the given IMSI.
+ *
+ * @hide
*/
public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
Objects.requireNonNull(subscriberId);
@@ -267,6 +362,7 @@ public class NetworkTemplate implements Parcelable {
*/
private final String[] mMatchSubscriberIds;
+ // TODO: Change variable name to match the Api surface.
private final String mNetworkId;
// Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
@@ -283,14 +379,14 @@ public class NetworkTemplate implements Parcelable {
// Bitfield containing OEM network properties{@code NetworkIdentity#OEM_*}.
private final int mOemManaged;
- private void checkValidSubscriberIdMatchRule() {
- switch (mMatchRule) {
+ private static void checkValidSubscriberIdMatchRule(int matchRule, int subscriberIdMatchRule) {
+ switch (matchRule) {
case MATCH_MOBILE:
case MATCH_CARRIER:
// MOBILE and CARRIER templates must always specify a subscriber ID.
- if (mSubscriberIdMatchRule == SUBSCRIBER_ID_MATCH_RULE_ALL) {
- throw new IllegalArgumentException("Invalid SubscriberIdMatchRule"
- + "on match rule: " + getMatchRuleName(mMatchRule));
+ if (subscriberIdMatchRule == SUBSCRIBER_ID_MATCH_RULE_ALL) {
+ throw new IllegalArgumentException("Invalid SubscriberIdMatchRule "
+ + "on match rule: " + getMatchRuleName(matchRule));
}
return;
default:
@@ -298,12 +394,14 @@ public class NetworkTemplate implements Parcelable {
}
}
+ /** @hide */
// TODO: Deprecate this constructor, mark it @UnsupportedAppUsage(maxTargetSdk = S)
@UnsupportedAppUsage
public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
}
+ /** @hide */
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
String networkId) {
// Older versions used to only match MATCH_MOBILE and MATCH_MOBILE_WILDCARD templates
@@ -316,6 +414,7 @@ public class NetworkTemplate implements Parcelable {
OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
+ /** @hide */
// TODO: Remove it after updating all of the caller.
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
String networkId, int metered, int roaming, int defaultNetwork, int subType,
@@ -324,6 +423,7 @@ public class NetworkTemplate implements Parcelable {
defaultNetwork, subType, oemManaged, SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
+ /** @hide */
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
String networkId, int metered, int roaming, int defaultNetwork, int subType,
int oemManaged, int subscriberIdMatchRule) {
@@ -339,7 +439,7 @@ public class NetworkTemplate implements Parcelable {
mSubType = subType;
mOemManaged = oemManaged;
mSubscriberIdMatchRule = subscriberIdMatchRule;
- checkValidSubscriberIdMatchRule();
+ checkValidSubscriberIdMatchRule(matchRule, subscriberIdMatchRule);
if (!isKnownMatchRule(matchRule)) {
throw new IllegalArgumentException("Unknown network template rule " + matchRule
+ " will not match any identity.");
@@ -360,7 +460,7 @@ public class NetworkTemplate implements Parcelable {
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mMatchRule);
dest.writeString(mSubscriberId);
dest.writeStringArray(mMatchSubscriberIds);
@@ -437,7 +537,7 @@ public class NetworkTemplate implements Parcelable {
return false;
}
- private String subscriberIdMatchRuleToString(int rule) {
+ private static String subscriberIdMatchRuleToString(int rule) {
switch (rule) {
case SUBSCRIBER_ID_MATCH_RULE_EXACT:
return "EXACT_MATCH";
@@ -448,6 +548,7 @@ public class NetworkTemplate implements Parcelable {
}
}
+ /** @hide */
public boolean isMatchRuleMobile() {
switch (mMatchRule) {
case MATCH_MOBILE:
@@ -458,48 +559,108 @@ public class NetworkTemplate implements Parcelable {
}
}
- public boolean isPersistable() {
+ /**
+ * Get match rule of the template. See {@code MATCH_*}.
+ */
+ @UnsupportedAppUsage
+ public int getMatchRule() {
+ // Wildcard rules are not exposed. For external callers, convert wildcard rules to
+ // exposed rules before returning.
switch (mMatchRule) {
case MATCH_MOBILE_WILDCARD:
+ return MATCH_MOBILE;
case MATCH_WIFI_WILDCARD:
- return false;
- case MATCH_CARRIER:
- return mSubscriberId != null;
- case MATCH_WIFI:
- if (Objects.equals(mNetworkId, WIFI_NETWORKID_ALL)
- && mSubscriberIdMatchRule == SUBSCRIBER_ID_MATCH_RULE_ALL) {
- return false;
- }
- return true;
+ return MATCH_WIFI;
default:
- return true;
+ return mMatchRule;
}
}
- @UnsupportedAppUsage
- public int getMatchRule() {
- return mMatchRule;
- }
-
+ /**
+ * Get subscriber Id of the template.
+ */
+ @Nullable
@UnsupportedAppUsage
public String getSubscriberId() {
return mSubscriberId;
}
+ /**
+ * Get set of subscriber Ids of the template.
+ */
+ @NonNull
+ public Set<String> getSubscriberIds() {
+ return new ArraySet<>(Arrays.asList(mMatchSubscriberIds));
+ }
+
+ /**
+ * Get Wifi Network Key of the template. See {@link WifiInfo#getCurrentNetworkKey()}.
+ */
+ @Nullable
+ public String getWifiNetworkKey() {
+ return mNetworkId;
+ }
+
+ /** @hide */
+ // TODO: Remove this and replace all callers with {@link #getWifiNetworkKey()}.
+ @Nullable
public String getNetworkId() {
return mNetworkId;
}
+ /**
+ * Get Subscriber Id Match Rule of the template.
+ *
+ * @hide
+ */
public int getSubscriberIdMatchRule() {
return mSubscriberIdMatchRule;
}
+ /**
+ * Get meteredness filter of the template.
+ */
+ @NetworkStats.Meteredness
public int getMeteredness() {
return mMetered;
}
/**
+ * Get roaming filter of the template.
+ */
+ @NetworkStats.Roaming
+ public int getRoaming() {
+ return mRoaming;
+ }
+
+ /**
+ * Get the default network status filter of the template.
+ */
+ @NetworkStats.DefaultNetwork
+ public int getDefaultNetworkStatus() {
+ return mDefaultNetwork;
+ }
+
+ /**
+ * Get the Radio Access Technology(RAT) type filter of the template.
+ */
+ public int getRatType() {
+ return mSubType;
+ }
+
+ /**
+ * Get the OEM managed filter of the template. See {@code OEM_MANAGED_*} or
+ * {@code android.net.NetworkIdentity#OEM_*}.
+ */
+ @OemManaged
+ public int getOemManaged() {
+ return mOemManaged;
+ }
+
+ /**
* Test if given {@link NetworkIdentity} matches this template.
+ *
+ * @hide
*/
public boolean matches(NetworkIdentity ident) {
if (!matchesMetered(ident)) return false;
@@ -565,6 +726,8 @@ public class NetworkTemplate implements Parcelable {
* Check if this template matches {@code subscriberId}. Returns true if this
* template was created with {@code SUBSCRIBER_ID_MATCH_RULE_ALL}, or with a
* {@code mMatchSubscriberIds} array that contains {@code subscriberId}.
+ *
+ * @hide
*/
public boolean matchesSubscriberId(@Nullable String subscriberId) {
return mSubscriberIdMatchRule == SUBSCRIBER_ID_MATCH_RULE_ALL
@@ -573,10 +736,10 @@ public class NetworkTemplate implements Parcelable {
/**
* Check if network with matching SSID. Returns true when the SSID matches, or when
- * {@code mNetworkId} is {@code WIFI_NETWORKID_ALL}.
+ * {@code mNetworkId} is {@code WIFI_NETWORK_KEY_ALL}.
*/
private boolean matchesWifiNetworkId(@Nullable String networkId) {
- return Objects.equals(mNetworkId, WIFI_NETWORKID_ALL)
+ return Objects.equals(mNetworkId, WIFI_NETWORK_KEY_ALL)
|| Objects.equals(sanitizeSsid(mNetworkId), sanitizeSsid(networkId));
}
@@ -599,10 +762,13 @@ public class NetworkTemplate implements Parcelable {
* The mapping is corresponding to {@code TelephonyManager#NETWORK_CLASS_BIT_MASK_*}.
*
* @param ratType An integer defined in {@code TelephonyManager#NETWORK_TYPE_*}.
+ *
+ * @hide
*/
// TODO: 1. Consider move this to TelephonyManager if used by other modules.
// 2. Consider make this configurable.
// 3. Use TelephonyManager APIs when available.
+ // TODO: @SystemApi when ready.
public static int getCollapsedRatType(int ratType) {
switch (ratType) {
case TelephonyManager.NETWORK_TYPE_GPRS:
@@ -639,7 +805,10 @@ public class NetworkTemplate implements Parcelable {
/**
* Return all supported collapsed RAT types that could be returned by
* {@link #getCollapsedRatType(int)}.
+ *
+ * @hide
*/
+ // TODO: @SystemApi when ready.
@NonNull
public static final int[] getAllCollapsedRatTypes() {
final int[] ratTypes = TelephonyManager.getAllNetworkTypes();
@@ -779,6 +948,8 @@ public class NetworkTemplate implements Parcelable {
* active merge set [A,B], we'd return a new template that primarily matches
* A, but also matches B.
* TODO: remove and use {@link #normalize(NetworkTemplate, List)}.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) {
@@ -797,7 +968,10 @@ public class NetworkTemplate implements Parcelable {
* For example, given an incoming template matching B, and the currently
* active merge set [A,B], we'd return a new template that primarily matches
* A, but also matches B.
+ *
+ * @hide
*/
+ // TODO: @SystemApi when ready.
public static NetworkTemplate normalize(NetworkTemplate template, List<String[]> mergedList) {
// Now there are several types of network which uses SubscriberId to store network
// information. For instances:
@@ -830,4 +1004,184 @@ public class NetworkTemplate implements Parcelable {
return new NetworkTemplate[size];
}
};
+
+ /**
+ * Builder class for NetworkTemplate.
+ */
+ public static final class Builder {
+ private final int mMatchRule;
+ // Use a SortedSet to provide a deterministic order when fetching the first one.
+ @NonNull
+ private final SortedSet<String> mMatchSubscriberIds = new TreeSet<>();
+ @Nullable
+ private String mWifiNetworkKey;
+
+ // Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
+ private int mMetered;
+ private int mRoaming;
+ private int mDefaultNetwork;
+ private int mRatType;
+
+ // Bitfield containing OEM network properties {@code NetworkIdentity#OEM_*}.
+ private int mOemManaged;
+
+ /**
+ * Creates a new Builder with given match rule to construct NetworkTemplate objects.
+ *
+ * @param matchRule the match rule of the template, see {@code MATCH_*}.
+ */
+ public Builder(@TemplateMatchRule final int matchRule) {
+ assertRequestableMatchRule(matchRule);
+ // Initialize members with default values.
+ mMatchRule = matchRule;
+ mWifiNetworkKey = WIFI_NETWORK_KEY_ALL;
+ mMetered = METERED_ALL;
+ mRoaming = ROAMING_ALL;
+ mDefaultNetwork = DEFAULT_NETWORK_ALL;
+ mRatType = NETWORK_TYPE_ALL;
+ mOemManaged = OEM_MANAGED_ALL;
+ }
+
+ /**
+ * Set the Subscriber Ids. Calling this function with an empty set represents
+ * the intention of matching any Subscriber Ids.
+ *
+ * @param subscriberIds the list of Subscriber Ids.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setSubscriberIds(@NonNull Set<String> subscriberIds) {
+ Objects.requireNonNull(subscriberIds);
+ mMatchSubscriberIds.clear();
+ mMatchSubscriberIds.addAll(subscriberIds);
+ return this;
+ }
+
+ /**
+ * Set the Wifi Network Key.
+ *
+ * @param wifiNetworkKey the Wifi Network Key, see {@link WifiInfo#getCurrentNetworkKey()}.
+ * Or null to match all networks.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setWifiNetworkKey(@Nullable String wifiNetworkKey) {
+ mWifiNetworkKey = wifiNetworkKey;
+ return this;
+ }
+
+ /**
+ * Set the meteredness filter.
+ *
+ * @param metered the meteredness filter.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setMeteredness(@NetworkStats.Meteredness int metered) {
+ mMetered = metered;
+ return this;
+ }
+
+ /**
+ * Set the roaming filter.
+ *
+ * @param roaming the roaming filter.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setRoaming(@NetworkStats.Roaming int roaming) {
+ mRoaming = roaming;
+ return this;
+ }
+
+ /**
+ * Set the default network status filter.
+ *
+ * @param defaultNetwork the default network status filter.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setDefaultNetworkStatus(@NetworkStats.DefaultNetwork int defaultNetwork) {
+ mDefaultNetwork = defaultNetwork;
+ return this;
+ }
+
+ /**
+ * Set the Radio Access Technology(RAT) type filter.
+ *
+ * @param ratType the Radio Access Technology(RAT) type filter. Use
+ * {@link #NETWORK_TYPE_ALL} to include all network types when filtering.
+ * See {@code TelephonyManager.NETWORK_TYPE_*}.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setRatType(@NetworkType int ratType) {
+ // Input will be validated with the match rule when building the template.
+ mRatType = ratType;
+ return this;
+ }
+
+ /**
+ * Set the OEM managed filter.
+ *
+ * @param oemManaged the match rule to match different type of OEM managed network or
+ * unmanaged networks. See {@code OEM_MANAGED_*}.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setOemManaged(@OemManaged int oemManaged) {
+ mOemManaged = oemManaged;
+ return this;
+ }
+
+ /**
+ * Check whether the match rule is requestable.
+ *
+ * @param matchRule the target match rule to be checked.
+ */
+ private static void assertRequestableMatchRule(final int matchRule) {
+ if (!isKnownMatchRule(matchRule)
+ || matchRule == MATCH_PROXY
+ || matchRule == MATCH_MOBILE_WILDCARD
+ || matchRule == MATCH_WIFI_WILDCARD) {
+ throw new IllegalArgumentException("Invalid match rule: "
+ + getMatchRuleName(matchRule));
+ }
+ }
+
+ private void assertRequestableParameters() {
+ // TODO: Check all the input are legitimate.
+ }
+
+ /**
+ * For backward compatibility, deduce match rule to a wildcard match rule
+ * if the Subscriber Ids are empty.
+ */
+ private int getWildcardDeducedMatchRule() {
+ if (mMatchRule == MATCH_MOBILE && mMatchSubscriberIds.isEmpty()) {
+ return MATCH_MOBILE_WILDCARD;
+ } else if (mMatchRule == MATCH_WIFI && mMatchSubscriberIds.isEmpty()
+ && mWifiNetworkKey == WIFI_NETWORK_KEY_ALL) {
+ return MATCH_WIFI_WILDCARD;
+ }
+ return mMatchRule;
+ }
+
+ /**
+ * Builds the instance of the NetworkTemplate.
+ *
+ * @return the built instance of NetworkTemplate.
+ */
+ @NonNull
+ public NetworkTemplate build() {
+ assertRequestableParameters();
+ final int subscriberIdMatchRule = mMatchSubscriberIds.isEmpty()
+ ? SUBSCRIBER_ID_MATCH_RULE_ALL : SUBSCRIBER_ID_MATCH_RULE_EXACT;
+ return new NetworkTemplate(getWildcardDeducedMatchRule(),
+ mMatchSubscriberIds.isEmpty() ? null : mMatchSubscriberIds.iterator().next(),
+ mMatchSubscriberIds.toArray(new String[0]),
+ mWifiNetworkKey, mMetered, mRoaming, mDefaultNetwork, mRatType, mOemManaged,
+ subscriberIdMatchRule);
+ }
+ }
}
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
index 7b8817692b74..97dfb64b33dd 100644
--- a/packages/ConnectivityT/service/Android.bp
+++ b/packages/ConnectivityT/service/Android.bp
@@ -61,11 +61,25 @@ filegroup {
],
}
+// Ethernet related libraries.
+
+filegroup {
+ name: "services.connectivity-ethernet-sources",
+ srcs: [
+ "src/com/android/server/net/IpConfigStore.java",
+ ],
+ path: "src",
+ visibility: [
+ "//frameworks/opt/net/ethernet",
+ ],
+}
+
// Connectivity-T common libraries.
filegroup {
name: "services.connectivity-tiramisu-sources",
srcs: [
+ ":services.connectivity-ethernet-sources",
":services.connectivity-ipsec-sources",
":services.connectivity-netstats-sources",
":services.connectivity-nsd-sources",
diff --git a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
index f251b86b7a09..d1e432e80f51 100644
--- a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
@@ -45,7 +45,6 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.TrafficStats;
-import android.net.util.NetdService;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -96,8 +95,6 @@ import java.util.Objects;
public class IpSecService extends IIpSecService.Stub {
private static final String TAG = "IpSecService";
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-
- private static final String NETD_SERVICE_NAME = "netd";
private static final int[] ADDRESS_FAMILIES =
new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
@@ -106,6 +103,8 @@ public class IpSecService extends IIpSecService.Stub {
@VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
+ private final INetd mNetd;
+
static {
try {
INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
@@ -627,16 +626,14 @@ public class IpSecService extends IIpSecService.Stub {
public void freeUnderlyingResources() {
int spi = mSpi.getSpi();
try {
- mDeps
- .getNetdInstance(mContext)
- .ipSecDeleteSecurityAssociation(
- mUid,
- mConfig.getSourceAddress(),
- mConfig.getDestinationAddress(),
- spi,
- mConfig.getMarkValue(),
- mConfig.getMarkMask(),
- mConfig.getXfrmInterfaceId());
+ mNetd.ipSecDeleteSecurityAssociation(
+ mUid,
+ mConfig.getSourceAddress(),
+ mConfig.getDestinationAddress(),
+ spi,
+ mConfig.getMarkValue(),
+ mConfig.getMarkMask(),
+ mConfig.getXfrmInterfaceId());
} catch (RemoteException | ServiceSpecificException e) {
Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
}
@@ -680,14 +677,12 @@ public class IpSecService extends IIpSecService.Stub {
private final String mSourceAddress;
private final String mDestinationAddress;
private int mSpi;
- private final Context mContext;
private boolean mOwnedByTransform = false;
- SpiRecord(Context context, int resourceId, String sourceAddress,
+ SpiRecord(int resourceId, String sourceAddress,
String destinationAddress, int spi) {
super(resourceId);
- mContext = context;
mSourceAddress = sourceAddress;
mDestinationAddress = destinationAddress;
mSpi = spi;
@@ -698,11 +693,9 @@ public class IpSecService extends IIpSecService.Stub {
public void freeUnderlyingResources() {
try {
if (!mOwnedByTransform) {
- mDeps
- .getNetdInstance(mContext)
- .ipSecDeleteSecurityAssociation(
- mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
- 0 /* mask */, 0 /* if_id */);
+ mNetd.ipSecDeleteSecurityAssociation(
+ mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
+ 0 /* mask */, 0 /* if_id */);
}
} catch (ServiceSpecificException | RemoteException e) {
Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
@@ -821,10 +814,8 @@ public class IpSecService extends IIpSecService.Stub {
private final int mIfId;
private Network mUnderlyingNetwork;
- private final Context mContext;
TunnelInterfaceRecord(
- Context context,
int resourceId,
String interfaceName,
Network underlyingNetwork,
@@ -835,7 +826,6 @@ public class IpSecService extends IIpSecService.Stub {
int intfId) {
super(resourceId);
- mContext = context;
mInterfaceName = interfaceName;
mUnderlyingNetwork = underlyingNetwork;
mLocalAddress = localAddr;
@@ -852,18 +842,17 @@ public class IpSecService extends IIpSecService.Stub {
// Teardown VTI
// Delete global policies
try {
- final INetd netd = mDeps.getNetdInstance(mContext);
- netd.ipSecRemoveTunnelInterface(mInterfaceName);
+ mNetd.ipSecRemoveTunnelInterface(mInterfaceName);
for (int selAddrFamily : ADDRESS_FAMILIES) {
- netd.ipSecDeleteSecurityPolicy(
+ mNetd.ipSecDeleteSecurityPolicy(
mUid,
selAddrFamily,
IpSecManager.DIRECTION_OUT,
mOkey,
0xffffffff,
mIfId);
- netd.ipSecDeleteSecurityPolicy(
+ mNetd.ipSecDeleteSecurityPolicy(
mUid,
selAddrFamily,
IpSecManager.DIRECTION_IN,
@@ -1026,7 +1015,6 @@ public class IpSecService extends IIpSecService.Stub {
static IpSecService create(Context context)
throws InterruptedException {
final IpSecService service = new IpSecService(context);
- service.connectNativeNetdService();
return service;
}
@@ -1057,8 +1045,13 @@ public class IpSecService extends IIpSecService.Stub {
@VisibleForTesting
public IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger) {
mContext = context;
- mDeps = deps;
+ mDeps = Objects.requireNonNull(deps, "Missing dependencies.");
mUidFdTagger = uidFdTagger;
+ try {
+ mNetd = mDeps.getNetdInstance(mContext);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** Called by system server when system is ready. */
@@ -1070,25 +1063,12 @@ public class IpSecService extends IIpSecService.Stub {
}
}
- private void connectNativeNetdService() {
- // Avoid blocking the system server to do this
- new Thread() {
- @Override
- public void run() {
- synchronized (IpSecService.this) {
- NetdService.get(NETD_FETCH_TIMEOUT_MS);
- }
- }
- }.start();
- }
-
synchronized boolean isNetdAlive() {
try {
- final INetd netd = mDeps.getNetdInstance(mContext);
- if (netd == null) {
+ if (mNetd == null) {
return false;
}
- return netd.isAlive();
+ return mNetd.isAlive();
} catch (RemoteException re) {
return false;
}
@@ -1149,15 +1129,12 @@ public class IpSecService extends IIpSecService.Stub {
IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
}
- spi =
- mDeps
- .getNetdInstance(mContext)
- .ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
+ spi = mNetd.ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Log.d(TAG, "Allocated SPI " + spi);
userRecord.mSpiRecords.put(
resourceId,
new RefcountedResource<SpiRecord>(
- new SpiRecord(mContext, resourceId, "",
+ new SpiRecord(resourceId, "",
destinationAddress, spi), binder));
} catch (ServiceSpecificException e) {
if (e.errorCode == OsConstants.ENOENT) {
@@ -1275,8 +1252,7 @@ public class IpSecService extends IIpSecService.Stub {
OsConstants.UDP_ENCAP,
OsConstants.UDP_ENCAP_ESPINUDP);
- mDeps.getNetdInstance(mContext).ipSecSetEncapSocketOwner(
- new ParcelFileDescriptor(sockFd), callingUid);
+ mNetd.ipSecSetEncapSocketOwner(new ParcelFileDescriptor(sockFd), callingUid);
if (port != 0) {
Log.v(TAG, "Binding to port " + port);
Os.bind(sockFd, INADDR_ANY, port);
@@ -1338,16 +1314,15 @@ public class IpSecService extends IIpSecService.Stub {
// Create VTI
// Add inbound/outbound global policies
// (use reqid = 0)
- final INetd netd = mDeps.getNetdInstance(mContext);
- netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
+ mNetd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
BinderUtils.withCleanCallingIdentity(() -> {
- NetdUtils.setInterfaceUp(netd, intfName);
+ NetdUtils.setInterfaceUp(mNetd, intfName);
});
for (int selAddrFamily : ADDRESS_FAMILIES) {
// Always send down correct local/remote addresses for template.
- netd.ipSecAddSecurityPolicy(
+ mNetd.ipSecAddSecurityPolicy(
callerUid,
selAddrFamily,
IpSecManager.DIRECTION_OUT,
@@ -1357,7 +1332,7 @@ public class IpSecService extends IIpSecService.Stub {
okey,
0xffffffff,
resourceId);
- netd.ipSecAddSecurityPolicy(
+ mNetd.ipSecAddSecurityPolicy(
callerUid,
selAddrFamily,
IpSecManager.DIRECTION_IN,
@@ -1377,7 +1352,7 @@ public class IpSecService extends IIpSecService.Stub {
//
// This is necessary only on the tunnel interface, and not any the interface to
// which traffic will be forwarded to.
- netd.ipSecAddSecurityPolicy(
+ mNetd.ipSecAddSecurityPolicy(
callerUid,
selAddrFamily,
IpSecManager.DIRECTION_FWD,
@@ -1393,7 +1368,6 @@ public class IpSecService extends IIpSecService.Stub {
resourceId,
new RefcountedResource<TunnelInterfaceRecord>(
new TunnelInterfaceRecord(
- mContext,
resourceId,
intfName,
underlyingNetwork,
@@ -1435,12 +1409,10 @@ public class IpSecService extends IIpSecService.Stub {
try {
// We can assume general validity of the IP address, since we get them as a
// LinkAddress, which does some validation.
- mDeps
- .getNetdInstance(mContext)
- .interfaceAddAddress(
- tunnelInterfaceInfo.mInterfaceName,
- localAddr.getAddress().getHostAddress(),
- localAddr.getPrefixLength());
+ mNetd.interfaceAddAddress(
+ tunnelInterfaceInfo.mInterfaceName,
+ localAddr.getAddress().getHostAddress(),
+ localAddr.getPrefixLength());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1464,9 +1436,7 @@ public class IpSecService extends IIpSecService.Stub {
try {
// We can assume general validity of the IP address, since we get them as a
// LinkAddress, which does some validation.
- mDeps
- .getNetdInstance(mContext)
- .interfaceDelAddress(
+ mNetd.interfaceDelAddress(
tunnelInterfaceInfo.mInterfaceName,
localAddr.getAddress().getHostAddress(),
localAddr.getPrefixLength());
@@ -1679,30 +1649,28 @@ public class IpSecService extends IIpSecService.Stub {
cryptName = crypt.getName();
}
- mDeps
- .getNetdInstance(mContext)
- .ipSecAddSecurityAssociation(
- Binder.getCallingUid(),
- c.getMode(),
- c.getSourceAddress(),
- c.getDestinationAddress(),
- (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
- spiRecord.getSpi(),
- c.getMarkValue(),
- c.getMarkMask(),
- (auth != null) ? auth.getName() : "",
- (auth != null) ? auth.getKey() : new byte[] {},
- (auth != null) ? auth.getTruncationLengthBits() : 0,
- cryptName,
- (crypt != null) ? crypt.getKey() : new byte[] {},
- (crypt != null) ? crypt.getTruncationLengthBits() : 0,
- (authCrypt != null) ? authCrypt.getName() : "",
- (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
- (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
- encapType,
- encapLocalPort,
- encapRemotePort,
- c.getXfrmInterfaceId());
+ mNetd.ipSecAddSecurityAssociation(
+ Binder.getCallingUid(),
+ c.getMode(),
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
+ spiRecord.getSpi(),
+ c.getMarkValue(),
+ c.getMarkMask(),
+ (auth != null) ? auth.getName() : "",
+ (auth != null) ? auth.getKey() : new byte[] {},
+ (auth != null) ? auth.getTruncationLengthBits() : 0,
+ cryptName,
+ (crypt != null) ? crypt.getKey() : new byte[] {},
+ (crypt != null) ? crypt.getTruncationLengthBits() : 0,
+ (authCrypt != null) ? authCrypt.getName() : "",
+ (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
+ (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
+ encapType,
+ encapLocalPort,
+ encapRemotePort,
+ c.getXfrmInterfaceId());
}
/**
@@ -1791,15 +1759,13 @@ public class IpSecService extends IIpSecService.Stub {
c.getMode() == IpSecTransform.MODE_TRANSPORT,
"Transform mode was not Transport mode; cannot be applied to a socket");
- mDeps
- .getNetdInstance(mContext)
- .ipSecApplyTransportModeTransform(
- socket,
- callingUid,
- direction,
- c.getSourceAddress(),
- c.getDestinationAddress(),
- info.getSpiRecord().getSpi());
+ mNetd.ipSecApplyTransportModeTransform(
+ socket,
+ callingUid,
+ direction,
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ info.getSpiRecord().getSpi());
}
/**
@@ -1811,9 +1777,7 @@ public class IpSecService extends IIpSecService.Stub {
@Override
public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
throws RemoteException {
- mDeps
- .getNetdInstance(mContext)
- .ipSecRemoveTransportModeTransform(socket);
+ mNetd.ipSecRemoveTransportModeTransform(socket);
}
/**
@@ -1888,18 +1852,16 @@ public class IpSecService extends IIpSecService.Stub {
// Always update the policy with the relevant XFRM_IF_ID
for (int selAddrFamily : ADDRESS_FAMILIES) {
- mDeps
- .getNetdInstance(mContext)
- .ipSecUpdateSecurityPolicy(
- callingUid,
- selAddrFamily,
- direction,
- transformInfo.getConfig().getSourceAddress(),
- transformInfo.getConfig().getDestinationAddress(),
- spi, // If outbound, also add SPI to the policy.
- mark, // Must always set policy mark; ikey/okey for VTIs
- 0xffffffff,
- c.getXfrmInterfaceId());
+ mNetd.ipSecUpdateSecurityPolicy(
+ callingUid,
+ selAddrFamily,
+ direction,
+ transformInfo.getConfig().getSourceAddress(),
+ transformInfo.getConfig().getDestinationAddress(),
+ spi, // If outbound, also add SPI to the policy.
+ mark, // Must always set policy mark; ikey/okey for VTIs
+ 0xffffffff,
+ c.getXfrmInterfaceId());
}
// Update SA with tunnel mark (ikey or okey based on direction)
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/packages/ConnectivityT/service/src/com/android/server/net/IpConfigStore.java
index d17dbde496ce..3a9a54415537 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/IpConfigStore.java
@@ -44,6 +44,9 @@ import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
+/**
+ * This class provides an API to store and manage L3 network IP configuration.
+ */
public class IpConfigStore {
private static final String TAG = "IpConfigStore";
private static final boolean DBG = false;
@@ -78,6 +81,9 @@ public class IpConfigStore {
return writeConfig(out, configKey, config, IPCONFIG_FILE_VERSION);
}
+ /**
+ * Write the IP configuration with the given parameters to {@link DataOutputStream}.
+ */
@VisibleForTesting
public static boolean writeConfig(DataOutputStream out, String configKey,
IpConfiguration config, int version) throws IOException {
@@ -154,10 +160,10 @@ public class IpConfigStore {
break;
case UNASSIGNED:
/* Ignore */
- break;
- default:
- loge("Ignore invalid proxy settings while writing");
- break;
+ break;
+ default:
+ loge("Ignore invalid proxy settings while writing");
+ break;
}
if (written) {
@@ -177,7 +183,7 @@ public class IpConfigStore {
}
/**
- * @Deprecated use {@link #writeIpConfigurations(String, ArrayMap)} instead.
+ * @deprecated use {@link #writeIpConfigurations(String, ArrayMap)} instead.
* New method uses string as network identifier which could be interface name or MAC address or
* other token.
*/
@@ -186,22 +192,28 @@ public class IpConfigStore {
final SparseArray<IpConfiguration> networks) {
mWriter.write(filePath, out -> {
out.writeInt(IPCONFIG_FILE_VERSION);
- for(int i = 0; i < networks.size(); i++) {
+ for (int i = 0; i < networks.size(); i++) {
writeConfig(out, String.valueOf(networks.keyAt(i)), networks.valueAt(i));
}
});
}
+ /**
+ * Write the IP configuration associated to the target networks to the destination path.
+ */
public void writeIpConfigurations(String filePath,
ArrayMap<String, IpConfiguration> networks) {
mWriter.write(filePath, out -> {
out.writeInt(IPCONFIG_FILE_VERSION);
- for(int i = 0; i < networks.size(); i++) {
+ for (int i = 0; i < networks.size(); i++) {
writeConfig(out, networks.keyAt(i), networks.valueAt(i));
}
});
}
+ /**
+ * Read the IP configuration from the destination path to {@link BufferedInputStream}.
+ */
public static ArrayMap<String, IpConfiguration> readIpConfigurations(String filePath) {
BufferedInputStream bufferedInputStream;
try {
@@ -215,7 +227,7 @@ public class IpConfigStore {
return readIpConfigurations(bufferedInputStream);
}
- /** @Deprecated use {@link #readIpConfigurations(String)} */
+ /** @deprecated use {@link #readIpConfigurations(String)} */
@Deprecated
public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(String filePath) {
BufferedInputStream bufferedInputStream;
@@ -230,7 +242,7 @@ public class IpConfigStore {
return readIpAndProxyConfigurations(bufferedInputStream);
}
- /** @Deprecated use {@link #readIpConfigurations(InputStream)} */
+ /** @deprecated use {@link #readIpConfigurations(InputStream)} */
@Deprecated
public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(
InputStream inputStream) {
@@ -420,7 +432,7 @@ public class IpConfigStore {
if (in != null) {
try {
in.close();
- } catch (Exception e) {}
+ } catch (Exception e) { }
}
}
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
index 3a70d657f62f..afeb24ad3b61 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
@@ -152,10 +152,22 @@ public class BannerMessagePreference extends Preference {
mPositiveButtonInfo.mButton = (Button) holder.findViewById(R.id.banner_positive_btn);
mNegativeButtonInfo.mButton = (Button) holder.findViewById(R.id.banner_negative_btn);
+ final Resources.Theme theme = context.getTheme();
+ @ColorInt final int accentColor =
+ context.getResources().getColor(mAttentionLevel.getAccentColorResId(), theme);
+
+ final ImageView iconView = (ImageView) holder.findViewById(R.id.banner_icon);
+ if (iconView != null) {
+ Drawable icon = getIcon();
+ iconView.setImageDrawable(
+ icon == null
+ ? getContext().getDrawable(R.drawable.ic_warning)
+ : icon);
+ iconView.setColorFilter(
+ new PorterDuffColorFilter(accentColor, PorterDuff.Mode.SRC_IN));
+ }
+
if (IS_AT_LEAST_S) {
- final Resources.Theme theme = context.getTheme();
- @ColorInt final int accentColor =
- context.getResources().getColor(mAttentionLevel.getAccentColorResId(), theme);
@ColorInt final int backgroundColor =
context.getResources().getColor(
mAttentionLevel.getBackgroundColorResId(), theme);
@@ -174,16 +186,6 @@ public class BannerMessagePreference extends Preference {
subtitleView.setText(mSubtitle);
subtitleView.setVisibility(mSubtitle == null ? View.GONE : View.VISIBLE);
- final ImageView iconView = (ImageView) holder.findViewById(R.id.banner_icon);
- if (iconView != null) {
- Drawable icon = getIcon();
- iconView.setImageDrawable(
- icon == null
- ? getContext().getDrawable(R.drawable.ic_warning)
- : icon);
- iconView.setColorFilter(
- new PorterDuffColorFilter(accentColor, PorterDuff.Mode.SRC_IN));
- }
} else {
holder.setDividerAllowedAbove(true);
holder.setDividerAllowedBelow(true);
@@ -323,7 +325,6 @@ public class BannerMessagePreference extends Preference {
/**
* Sets the attention level. This will update the color theme of the preference.
*/
- @RequiresApi(Build.VERSION_CODES.S)
public BannerMessagePreference setAttentionLevel(AttentionLevel attentionLevel) {
if (attentionLevel == mAttentionLevel) {
return this;
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 08e491db7247..38ff18a6788d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -85,6 +85,8 @@ public class GlobalSettingsValidators {
VALIDATORS.put(Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, ANY_STRING_VALIDATOR);
VALIDATORS.put(
Global.EMERGENCY_TONE, new DiscreteValueValidator(new String[] {"0", "1", "2"}));
+ VALIDATORS.put(Global.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
+ NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Global.CALL_AUTO_RETRY, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.DOCK_AUDIO_MEDIA_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index e76e51db05ea..dd1cb6b32d4f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -276,8 +276,6 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_SOUND_ENABLED, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
- NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 0dfad172ec5b..2d1f0cb8e7c4 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -257,6 +257,7 @@ public class SettingsBackupTest {
Settings.Global.DROPBOX_RESERVE_PERCENT,
Settings.Global.DROPBOX_TAG_PREFIX,
Settings.Global.EMERGENCY_AFFORDANCE_NEEDED,
+ Settings.Global.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
Settings.Global.EMULATE_DISPLAY_CUTOUT,
Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED,
Settings.Global.ENABLE_CACHE_QUOTA_CALCULATION,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b25e9a18fc3d..10252ee3bc60 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -207,6 +207,7 @@
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
<uses-permission android:name="android.permission.QUERY_ADMIN_POLICY" />
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES" />
<uses-permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" />
<uses-permission android:name="android.permission.CLEAR_FREEZE_PERIOD" />
<uses-permission android:name="android.permission.MODIFY_QUIET_MODE" />
@@ -609,6 +610,9 @@
<!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
<uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
+ <!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
+ <uses-permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" />
+
<!-- Permission required for CTS test - CommunalManagerTest -->
<uses-permission android:name="android.permission.WRITE_COMMUNAL_STATE" />
<uses-permission android:name="android.permission.READ_COMMUNAL_STATE" />
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index a16f5cd5d930..da9a92a1f6b4 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -20,9 +20,11 @@ import android.app.PendingIntent;
import android.app.smartspace.SmartspaceAction;
import android.app.smartspace.SmartspaceTarget;
import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -39,6 +41,7 @@ import java.util.List;
public interface BcSmartspaceDataPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
int VERSION = 1;
+ String TAG = "BcSmartspaceDataPlugin";
/** Register a listener to get Smartspace data. */
void registerListener(SmartspaceTargetListener listener);
@@ -124,10 +127,14 @@ public interface BcSmartspaceDataPlugin extends Plugin {
/** Interface for launching Intents, which can differ on the lockscreen */
interface IntentStarter {
default void startFromAction(SmartspaceAction action, View v, boolean showOnLockscreen) {
- if (action.getIntent() != null) {
- startIntent(v, action.getIntent(), showOnLockscreen);
- } else if (action.getPendingIntent() != null) {
- startPendingIntent(action.getPendingIntent(), showOnLockscreen);
+ try {
+ if (action.getIntent() != null) {
+ startIntent(v, action.getIntent(), showOnLockscreen);
+ } else if (action.getPendingIntent() != null) {
+ startPendingIntent(action.getPendingIntent(), showOnLockscreen);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Could not launch intent for action: " + action, e);
}
}
diff --git a/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_header_bg.xml b/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_header_bg.xml
index 177f69590882..11199358b6ef 100644
--- a/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_header_bg.xml
+++ b/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_header_bg.xml
@@ -17,14 +17,14 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:paddingMode="stack"
- android:paddingStart="44dp"
+ android:paddingStart="24dp"
android:paddingEnd="44dp"
android:paddingLeft="0dp"
android:paddingRight="0dp">
<item>
<shape android:shape="rectangle">
<solid android:color="?androidprv:attr/colorSurface" />
- <corners android:radius="@dimen/keyguard_user_switcher_corner" />
+ <corners android:radius="32dp" />
</shape>
</item>
<item
diff --git a/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_item_selected_bg.xml b/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_item_selected_bg.xml
new file mode 100644
index 000000000000..5bb5690b8f34
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_item_selected_bg.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape android:shape="rectangle"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <solid android:color="?androidprv:attr/colorAccentPrimary" />
+ <corners android:radius="24dp" />
+</shape>
diff --git a/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_popup_bg.xml b/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_popup_bg.xml
index 96a2d1534ebe..74ece15aa78c 100644
--- a/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_popup_bg.xml
+++ b/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_popup_bg.xml
@@ -18,5 +18,5 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<solid android:color="?androidprv:attr/colorSurface" />
- <corners android:radius="@dimen/keyguard_user_switcher_popup_corner" />
+ <corners android:radius="28dp" />
</shape>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
index 4f0925f3bfbb..36035fc87e40 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
@@ -30,8 +30,8 @@
<ImageView
android:id="@+id/user_icon"
- android:layout_width="@dimen/keyguard_user_switcher_icon_size"
- android:layout_height="@dimen/keyguard_user_switcher_icon_size" />
+ android:layout_width="@dimen/bouncer_user_switcher_icon_size"
+ android:layout_height="@dimen/bouncer_user_switcher_icon_size" />
<!-- need to keep this outer view in order to have a correctly sized anchor
for the dropdown menu, as well as dropdown background in the right place -->
@@ -40,13 +40,12 @@
android:orientation="horizontal"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:layout_marginTop="30dp"
- android:minHeight="48dp">
+ android:layout_marginTop="30dp">
<TextView
- style="@style/Keyguard.UserSwitcher.Spinner.Header"
+ style="@style/Bouncer.UserSwitcher.Spinner.Header"
android:clickable="false"
android:id="@+id/user_switcher_header"
- android:layout_width="@dimen/keyguard_user_switcher_width"
+ android:layout_width="@dimen/bouncer_user_switcher_width"
android:layout_height="wrap_content" />
</LinearLayout>>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
index b08e1ff4c472..c388f15be6e4 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
@@ -13,13 +13,14 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<TextView
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/Keyguard.UserSwitcher.Spinner.Item"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="start"
- android:paddingStart="@dimen/control_menu_horizontal_padding"
- android:paddingEnd="@dimen/control_menu_horizontal_padding"
- android:textDirection="locale"/>
-
+ android:layout_height="wrap_content">
+ <TextView
+ style="@style/Bouncer.UserSwitcher.Spinner.Item"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="12dp"
+ android:layout_marginEnd="12dp" />
+</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 2819dc9c27b7..c8bb8e99be17 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -108,11 +108,15 @@
<dimen name="one_handed_bouncer_move_animation_translation">120dp</dimen>
- <dimen name="keyguard_user_switcher_header_text_size">32sp</dimen>
- <dimen name="keyguard_user_switcher_item_text_size">32sp</dimen>
- <dimen name="keyguard_user_switcher_width">320dp</dimen>
- <dimen name="keyguard_user_switcher_icon_size">310dp</dimen>
- <dimen name="keyguard_user_switcher_corner">32dp</dimen>
- <dimen name="keyguard_user_switcher_popup_corner">24dp</dimen>
- <dimen name="keyguard_user_switcher_item_padding_vertical">15dp</dimen>
+ <dimen name="bouncer_user_switcher_header_text_size">20sp</dimen>
+ <dimen name="bouncer_user_switcher_item_text_size">20sp</dimen>
+ <dimen name="bouncer_user_switcher_item_line_height">24sp</dimen>
+ <dimen name="bouncer_user_switcher_item_icon_size">28dp</dimen>
+ <dimen name="bouncer_user_switcher_item_icon_padding">12dp</dimen>
+ <dimen name="bouncer_user_switcher_width">248dp</dimen>
+ <dimen name="bouncer_user_switcher_icon_size">190dp</dimen>
+ <dimen name="bouncer_user_switcher_popup_header_height">12dp</dimen>
+ <dimen name="bouncer_user_switcher_popup_divider_height">4dp</dimen>
+ <dimen name="bouncer_user_switcher_item_padding_vertical">10dp</dimen>
+ <dimen name="bouncer_user_switcher_item_padding_horizontal">12dp</dimen>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 60f034ae7ee8..5048f856ac70 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -141,22 +141,23 @@
<item name="android:shadowRadius">0</item>
</style>
- <style name="Keyguard.UserSwitcher.Spinner" parent="@android:style/Widget.DeviceDefault.TextView">
+ <style name="Bouncer.UserSwitcher.Spinner" parent="@android:style/Widget.DeviceDefault.TextView">
<item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
- <item name="android:paddingTop">@dimen/keyguard_user_switcher_item_padding_vertical</item>
- <item name="android:paddingBottom">@dimen/keyguard_user_switcher_item_padding_vertical</item>
+ <item name="android:minHeight">48dp</item>
+ <item name="android:paddingVertical">@dimen/bouncer_user_switcher_item_padding_vertical</item>
+ <item name="android:paddingHorizontal">@dimen/bouncer_user_switcher_item_padding_horizontal</item>
+ <item name="android:lineHeight">@dimen/bouncer_user_switcher_item_line_height</item>
+ <item name="android:gravity">start|center_vertical</item>
</style>
- <style name="Keyguard.UserSwitcher.Spinner.Header">
- <item name="android:background">@drawable/keyguard_user_switcher_header_bg</item>
- <item name="android:textSize">@dimen/keyguard_user_switcher_header_text_size</item>
+ <style name="Bouncer.UserSwitcher.Spinner.Header">
+ <item name="android:background">@drawable/bouncer_user_switcher_header_bg</item>
+ <item name="android:textSize">@dimen/bouncer_user_switcher_header_text_size</item>
</style>
- <style name="Keyguard.UserSwitcher.Spinner.Item">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textSize">@dimen/keyguard_user_switcher_item_text_size</item>
+ <style name="Bouncer.UserSwitcher.Spinner.Item">
+ <item name="android:textSize">@dimen/bouncer_user_switcher_item_text_size</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/drawable/ic_circle_check_box.xml b/packages/SystemUI/res/drawable/ic_circle_check_box.xml
new file mode 100644
index 000000000000..b44a32dda172
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_circle_check_box.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/checked"
+ android:state_checked="true"
+ android:drawable="@drawable/media_output_status_check" />
+ <item
+ android:id="@+id/unchecked"
+ android:state_checked="false"
+ android:drawable="@drawable/ic_circular_unchecked" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/ic_circular_unchecked.xml b/packages/SystemUI/res/drawable/ic_circular_unchecked.xml
new file mode 100644
index 000000000000..9b43cf64f116
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_circular_unchecked.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@color/media_dialog_inactive_item_main_content"
+ android:pathData="M12,22q-2.075,0 -3.9,-0.788 -1.825,-0.787 -3.175,-2.137 -1.35,-1.35 -2.137,-3.175Q2,14.075 2,12t0.788,-3.9q0.787,-1.825 2.137,-3.175 1.35,-1.35 3.175,-2.137Q9.925,2 12,2t3.9,0.788q1.825,0.787 3.175,2.137 1.35,1.35 2.137,3.175Q22,9.925 22,12t-0.788,3.9q-0.787,1.825 -2.137,3.175 -1.35,1.35 -3.175,2.137Q14.075,22 12,22zM12,12zM12,20q3.325,0 5.663,-2.337Q20,15.325 20,12t-2.337,-5.662Q15.325,4 12,4T6.338,6.338Q4,8.675 4,12q0,3.325 2.338,5.663Q8.675,20 12,20z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index 1f10e5dfeed8..405863dc9d92 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -101,4 +101,16 @@
app:layout_constraintBottom_toBottomOf="parent"
/>
+ <FrameLayout
+ android:id="@+id/privacy_container"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:gravity="center"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="@id/date"
+ >
+ <include layout="@layout/ongoing_privacy_chip"/>
+ </FrameLayout>
+
</androidx.constraintlayout.motion.widget.MotionLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 275e0a52ab45..baf533656822 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -390,36 +390,34 @@
android:focusable="false">
<FrameLayout
- android:id="@+id/apm_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:clickable="true"
- android:focusable="true"
android:layout_gravity="start|center_vertical"
android:orientation="vertical">
<Button
+ android:id="@+id/apm_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/turn_off_airplane_mode"
android:ellipsize="end"
style="@style/Widget.Dialog.Button.BorderButton"
- android:clickable="false"/>
+ android:clickable="true"
+ android:focusable="true"/>
</FrameLayout>
<FrameLayout
- android:id="@+id/done_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:layout_gravity="end|center_vertical"
- android:clickable="true"
- android:focusable="true">
+ android:layout_gravity="end|center_vertical">
<Button
+ android:id="@+id/done_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/inline_done_button"
style="@style/Widget.Dialog.Button"
- android:clickable="false"/>
+ android:clickable="true"
+ android:focusable="true"/>
</FrameLayout>
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index 89316893cead..806804f2fc47 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -96,26 +96,6 @@
android:textSize="14sp"
android:fontFamily="@*android:string/config_bodyFontFamily"
android:visibility="gone"/>
- <ImageView
- android:id="@+id/add_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="right"
- android:layout_marginEnd="16dp"
- android:layout_alignParentRight="true"
- android:src="@drawable/ic_add"
- android:tint="?android:attr/colorAccent"
- />
- <CheckBox
- android:id="@+id/check_box"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="right"
- android:layout_marginEnd="16dp"
- android:layout_alignParentRight="true"
- android:button="@drawable/ic_check_box"
- android:visibility="gone"
- />
</RelativeLayout>
<ProgressBar
@@ -139,5 +119,15 @@
android:indeterminateOnly="true"
android:importantForAccessibility="no"
android:visibility="gone"/>
+
+ <CheckBox
+ android:id="@+id/check_box"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="16dp"
+ android:layout_gravity="right|center"
+ android:button="@drawable/ic_circle_check_box"
+ android:visibility="gone"
+ />
</FrameLayout>
</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_ttt_chip.xml b/packages/SystemUI/res/layout/media_ttt_chip.xml
index 6fbc41c7e20d..2d082dc7d5e2 100644
--- a/packages/SystemUI/res/layout/media_ttt_chip.xml
+++ b/packages/SystemUI/res/layout/media_ttt_chip.xml
@@ -26,6 +26,13 @@
android:gravity="center_vertical"
>
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/app_icon"
+ android:layout_width="@dimen/media_ttt_icon_size"
+ android:layout_height="@dimen/media_ttt_icon_size"
+ android:layout_marginEnd="12dp"
+ />
+
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
@@ -37,8 +44,8 @@
<ProgressBar
android:id="@+id/loading"
android:indeterminate="true"
- android:layout_width="@dimen/media_ttt_icon_size"
- android:layout_height="@dimen/media_ttt_icon_size"
+ android:layout_width="@dimen/media_ttt_loading_size"
+ android:layout_height="@dimen/media_ttt_loading_size"
android:layout_marginStart="12dp"
android:indeterminateTint="?androidprv:attr/colorAccentPrimaryVariant"
style="?android:attr/progressBarStyleSmall"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 3695286a77da..9f017b29c665 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -714,19 +714,19 @@
<bool name="config_enablePrivacyDot">true</bool>
<!-- The positions widgets can be in defined as View.Gravity constants -->
- <integer-array name="config_dreamOverlayPositions">
+ <integer-array name="config_dreamComplicationPositions">
</integer-array>
- <!-- Widget components to show as dream overlays -->
- <string-array name="config_dreamOverlayComponents" translatable="false">
+ <!-- Widget components to show as dream complications -->
+ <string-array name="config_dreamAppWidgetComplications" translatable="false">
</string-array>
- <!-- Width percentage of dream overlay components -->
- <item name="config_dreamOverlayComponentWidthPercent" translatable="false" format="float"
+ <!-- Width percentage of dream complications -->
+ <item name="config_dreamComplicationWidthPercent" translatable="false" format="float"
type="dimen">0.33</item>
- <!-- Height percentage of dream overlay components -->
- <item name="config_dreamOverlayComponentHeightPercent" translatable="false" format="float"
+ <!-- Height percentage of dream complications -->
+ <item name="config_dreamComplicationHeightPercent" translatable="false" format="float"
type="dimen">0.25</item>
<!-- Flag to enable dream overlay service and its registration -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d4398a8d35a2..b7f25e81046c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -30,10 +30,10 @@
<dimen name="navigation_bar_deadzone_size_max">32dp</dimen>
<!-- dimensions for the navigation bar handle -->
- <dimen name="navigation_handle_radius">2dp</dimen>
- <dimen name="navigation_handle_bottom">10dp</dimen>
+ <dimen name="navigation_handle_radius">1dp</dimen>
+ <dimen name="navigation_handle_bottom">6dp</dimen>
<dimen name="navigation_handle_sample_horizontal_margin">10dp</dimen>
- <dimen name="navigation_home_handle_width">108dp</dimen>
+ <dimen name="navigation_home_handle_width">72dp</dimen>
<!-- Size of the nav bar edge panels, should be greater to the
edge sensitivity + the drag threshold -->
@@ -980,7 +980,8 @@
<!-- Media tap-to-transfer chip -->
<dimen name="media_ttt_chip_outer_padding">16dp</dimen>
<dimen name="media_ttt_text_size">16sp</dimen>
- <dimen name="media_ttt_icon_size">16dp</dimen>
+ <dimen name="media_ttt_icon_size">24dp</dimen>
+ <dimen name="media_ttt_loading_size">20dp</dimen>
<dimen name="media_ttt_undo_button_vertical_padding">8dp</dimen>
<dimen name="media_ttt_undo_button_vertical_negative_margin">-8dp</dimen>
diff --git a/packages/SystemUI/res/xml/combined_qs_header_scene.xml b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
index d61e4a95bb68..91607d2f9bea 100644
--- a/packages/SystemUI/res/xml/combined_qs_header_scene.xml
+++ b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
@@ -23,11 +23,22 @@
app:constraintSetEnd="@id/qs_header_constraint"
app:constraintSetStart="@id/qqs_header_constraint">
<KeyFrameSet>
+ <!-- These positions are to prevent visual movement of @id/date -->
<KeyPosition
app:keyPositionType="pathRelative"
app:percentX="0"
- app:framePosition="50"
+ app:framePosition="49"
app:motionTarget="@id/date" />
+ <KeyPosition
+ app:keyPositionType="pathRelative"
+ app:percentX="1"
+ app:framePosition="51"
+ app:motionTarget="@id/date" />
+ <KeyAttribute
+ app:motionTarget="@id/date"
+ app:framePosition="50"
+ android:alpha="0"
+ />
</KeyFrameSet>
</Transition>
diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml
index 3d7b549fc54b..c5b4c5d776b9 100644
--- a/packages/SystemUI/res/xml/qqs_header.xml
+++ b/packages/SystemUI/res/xml/qqs_header.xml
@@ -49,6 +49,14 @@
</Constraint>
<Constraint
+ android:id="@+id/statusIcons">
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/batteryRemainingIcon" >
+ </Constraint>
+
+ <Constraint
android:id="@+id/carrier_group">
<CustomAttribute
app:attributeName="alpha"
@@ -56,6 +64,15 @@
/>
</Constraint>
-
+ <Constraint
+ android:id="@+id/privacy_container">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="@id/date"
+ />
+ </Constraint>
</ConstraintSet> \ No newline at end of file
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
index 6a0ab866966c..8248fcdb50fb 100644
--- a/packages/SystemUI/res/xml/qs_header.xml
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -58,5 +58,14 @@
/>
</Constraint>
-
+ <Constraint
+ android:id="@+id/privacy_container">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="@id/date"
+ />
+ </Constraint>
</ConstraintSet> \ No newline at end of file
diff --git a/packages/SystemUI/res/xml/split_header.xml b/packages/SystemUI/res/xml/split_header.xml
index 44d42a05cd46..03401b3d51d1 100644
--- a/packages/SystemUI/res/xml/split_header.xml
+++ b/packages/SystemUI/res/xml/split_header.xml
@@ -53,5 +53,29 @@
/>
</Constraint>
+ <Constraint
+ android:id="@+id/batteryRemainingIcon">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
+ app:layout_constraintStart_toEndOf="@id/statusIcons"
+ app:layout_constraintEnd_toStartOf="@id/privacy_container"
+ app:layout_constraintTop_toTopOf="@id/clock"
+ app:layout_constraintBottom_toBottomOf="parent"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/privacy_container">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="@id/date"
+ app:layout_constraintStart_toEndOf="@id/batteryRemainingIcon"
+ />
+ </Constraint>
</ConstraintSet> \ No newline at end of file
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 38eded878014..48fcbbda7e46 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
@@ -61,7 +61,7 @@ import java.util.function.Consumer;
public class ActivityManagerWrapper {
private static final String TAG = "ActivityManagerWrapper";
-
+ private static final int NUM_RECENT_ACTIVITIES_REQUEST = 3;
private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper();
// Should match the values in PhoneWindowManager
@@ -113,6 +113,22 @@ public class ActivityManagerWrapper {
}
/**
+ * We ask for {@link #NUM_RECENT_ACTIVITIES_REQUEST} activities because when in split screen,
+ * we'll get back 2 activities for each split app and one for launcher. Launcher might be more
+ * "recently" used than one of the split apps so if we only request 2 tasks, then we might miss
+ * out on one of the split apps
+ *
+ * @return an array of up to {@link #NUM_RECENT_ACTIVITIES_REQUEST} running tasks
+ * filtering only for tasks that can be visible in the recent tasks list.
+ */
+ public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) {
+ // Note: The set of running tasks from the system is ordered by recency
+ List<ActivityManager.RunningTaskInfo> tasks =
+ mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST, filterOnlyVisibleRecents);
+ return tasks.toArray(new RunningTaskInfo[tasks.size()]);
+ }
+
+ /**
* @return a list of the recents tasks.
*/
public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index b84cb19b9468..b5ea498f185d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -32,9 +32,14 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BlendMode;
import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
+import android.os.UserManager;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
@@ -70,6 +75,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.UserIcons;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.settingslib.Utils;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
@@ -760,11 +766,11 @@ public class KeyguardSecurityContainer extends FrameLayout {
private ViewGroup mView;
private ViewGroup mUserSwitcherViewGroup;
private KeyguardSecurityViewFlipper mViewFlipper;
- private ImageView mUserIconView;
private TextView mUserSwitcher;
private FalsingManager mFalsingManager;
private UserSwitcherController mUserSwitcherController;
private KeyguardUserSwitcherPopupMenu mPopup;
+ private Resources mResources;
@Override
public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
@@ -775,6 +781,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
mViewFlipper = viewFlipper;
mFalsingManager = falsingManager;
mUserSwitcherController = userSwitcherController;
+ mResources = v.getContext().getResources();
if (mUserSwitcherViewGroup == null) {
LayoutInflater.from(v.getContext()).inflate(
@@ -784,9 +791,8 @@ public class KeyguardSecurityContainer extends FrameLayout {
mUserSwitcherViewGroup = mView.findViewById(R.id.keyguard_bouncer_user_switcher);
}
- mUserIconView = mView.findViewById(R.id.user_icon);
- Drawable icon = UserIcons.getDefaultUserIcon(v.getContext().getResources(), 0, false);
- mUserIconView.setImageDrawable(icon);
+ Drawable userIcon = findUserIcon(KeyguardUpdateMonitor.getCurrentUser());
+ ((ImageView) mView.findViewById(R.id.user_icon)).setImageDrawable(userIcon);
updateSecurityViewLocation();
@@ -802,6 +808,14 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
}
+ private Drawable findUserIcon(int userId) {
+ Bitmap userIcon = UserManager.get(mView.getContext()).getUserIcon(userId);
+ if (userIcon != null) {
+ return new BitmapDrawable(userIcon);
+ }
+ return UserIcons.getDefaultUserIcon(mResources, userId, false);
+ }
+
@Override
public void startAppearAnimation(SecurityMode securityMode) {
// IME insets animations handle alpha and translation
@@ -824,8 +838,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
return;
}
- int yTranslation = mView.getContext().getResources().getDimensionPixelSize(
- R.dimen.disappear_y_translation);
+ int yTranslation = mResources.getDimensionPixelSize(R.dimen.disappear_y_translation);
AnimatorSet anims = new AnimatorSet();
ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
@@ -840,21 +853,70 @@ public class KeyguardSecurityContainer extends FrameLayout {
String currentUserName = mUserSwitcherController.getCurrentUserName();
mUserSwitcher.setText(currentUserName);
+ final UserRecord currentUser = getCurrentUser();
ViewGroup anchor = mView.findViewById(R.id.user_switcher_anchor);
BaseUserAdapter adapter = new BaseUserAdapter(mUserSwitcherController) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
UserRecord item = getItem(position);
- TextView view = (TextView) convertView;
+ FrameLayout view = (FrameLayout) convertView;
if (view == null) {
- view = (TextView) LayoutInflater.from(parent.getContext()).inflate(
+ view = (FrameLayout) LayoutInflater.from(parent.getContext()).inflate(
R.layout.keyguard_bouncer_user_switcher_item,
parent,
false);
}
- view.setText(getName(parent.getContext(), item));
+ TextView textView = (TextView) view.getChildAt(0);
+ textView.setText(getName(parent.getContext(), item));
+ Drawable icon = null;
+ if (item.picture != null) {
+ icon = new BitmapDrawable(item.picture);
+ } else {
+ icon = getDrawable(item, view.getContext());
+ }
+ int iconSize = view.getResources().getDimensionPixelSize(
+ R.dimen.bouncer_user_switcher_item_icon_size);
+ int iconPadding = view.getResources().getDimensionPixelSize(
+ R.dimen.bouncer_user_switcher_item_icon_padding);
+ icon.setBounds(0, 0, iconSize, iconSize);
+ textView.setCompoundDrawablePadding(iconPadding);
+ textView.setCompoundDrawablesRelative(icon, null, null, null);
+
+ if (item == currentUser) {
+ textView.setBackground(view.getContext().getDrawable(
+ R.drawable.bouncer_user_switcher_item_selected_bg));
+ } else {
+ textView.setBackground(null);
+ }
return view;
}
+
+ private Drawable getDrawable(UserRecord item, Context context) {
+ Drawable drawable;
+ if (item.isCurrent && item.isGuest) {
+ drawable = context.getDrawable(R.drawable.ic_avatar_guest_user);
+ } else {
+ drawable = getIconDrawable(context, item);
+ }
+
+ int iconColor;
+ if (item.isSwitchToEnabled) {
+ iconColor = Utils.getColorAttrDefaultColor(context,
+ com.android.internal.R.attr.colorAccentPrimaryVariant);
+ } else {
+ iconColor = context.getResources().getColor(
+ R.color.kg_user_switcher_restricted_avatar_icon_color,
+ context.getTheme());
+ }
+ drawable.setTint(iconColor);
+
+ Drawable bg = context.getDrawable(R.drawable.kg_bg_avatar);
+ bg.setTintBlendMode(BlendMode.DST);
+ bg.setTint(Utils.getColorAttrDefaultColor(context,
+ com.android.internal.R.attr.colorSurfaceVariant));
+ drawable = new LayerDrawable(new Drawable[]{bg, drawable});
+ return drawable;
+ }
};
if (adapter.getCount() < 2) {
@@ -876,7 +938,8 @@ public class KeyguardSecurityContainer extends FrameLayout {
public void onItemClick(AdapterView parent, View view, int pos, long id) {
if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
- UserRecord user = adapter.getItem(pos);
+ // Subtract one for the header
+ UserRecord user = adapter.getItem(pos - 1);
if (!user.isCurrent) {
adapter.onUserListItemClicked(user);
}
@@ -888,6 +951,16 @@ public class KeyguardSecurityContainer extends FrameLayout {
});
}
+ private UserRecord getCurrentUser() {
+ for (int i = 0; i < mUserSwitcherController.getUsers().size(); ++i) {
+ UserRecord userRecord = mUserSwitcherController.getUsers().get(i);
+ if (userRecord.isCurrent) {
+ return userRecord;
+ }
+ }
+ return null;
+ }
+
/**
* Each view will get half the width. Yes, it would be easier to use something other than
* FrameLayout but it was too disruptive to downstream projects to change.
@@ -901,8 +974,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
@Override
public void updateSecurityViewLocation() {
- if (mView.getContext().getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_PORTRAIT) {
+ if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
updateViewGravity(mViewFlipper, Gravity.CENTER_HORIZONTAL);
updateViewGravity(mUserSwitcherViewGroup, Gravity.CENTER_HORIZONTAL);
mUserSwitcherViewGroup.setTranslationY(0);
@@ -912,8 +984,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
// Attempt to reposition a bit higher to make up for this frame being a bit lower
// on the device
- int yTrans = mView.getContext().getResources().getDimensionPixelSize(
- R.dimen.status_bar_height);
+ int yTrans = mResources.getDimensionPixelSize(R.dimen.status_bar_height);
mUserSwitcherViewGroup.setTranslationY(-yTrans);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
index 7b6ce3e1c951..efa5558f5088 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
@@ -18,6 +18,8 @@ package com.android.keyguard;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.drawable.ShapeDrawable;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListPopupWindow;
@@ -32,15 +34,6 @@ import com.android.systemui.plugins.FalsingManager;
public class KeyguardUserSwitcherPopupMenu extends ListPopupWindow {
private Context mContext;
private FalsingManager mFalsingManager;
- private int mLastHeight = -1;
- private View.OnLayoutChangeListener mLayoutListener = (v, l, t, r, b, ol, ot, or, ob) -> {
- int height = -v.getMeasuredHeight() + getAnchorView().getHeight();
- if (height != mLastHeight) {
- mLastHeight = height;
- setVerticalOffset(height);
- KeyguardUserSwitcherPopupMenu.super.show();
- }
- };
public KeyguardUserSwitcherPopupMenu(@NonNull Context context,
@NonNull FalsingManager falsingManager) {
@@ -49,7 +42,7 @@ public class KeyguardUserSwitcherPopupMenu extends ListPopupWindow {
mFalsingManager = falsingManager;
Resources res = mContext.getResources();
setBackgroundDrawable(
- res.getDrawable(R.drawable.keyguard_user_switcher_popup_bg, context.getTheme()));
+ res.getDrawable(R.drawable.bouncer_user_switcher_popup_bg, context.getTheme()));
setModal(true);
setOverlapAnchor(true);
}
@@ -63,8 +56,20 @@ public class KeyguardUserSwitcherPopupMenu extends ListPopupWindow {
super.show();
ListView listView = getListView();
- // This will force the popupwindow to show upward instead of drop down
- listView.addOnLayoutChangeListener(mLayoutListener);
+ listView.setVerticalScrollBarEnabled(false);
+ listView.setHorizontalScrollBarEnabled(false);
+
+ // Creates a transparent spacer between items
+ ShapeDrawable shape = new ShapeDrawable();
+ shape.setAlpha(0);
+ listView.setDivider(shape);
+ listView.setDividerHeight(mContext.getResources().getDimensionPixelSize(
+ R.dimen.bouncer_user_switcher_popup_divider_height));
+
+ int height = mContext.getResources().getDimensionPixelSize(
+ R.dimen.bouncer_user_switcher_popup_header_height);
+ listView.addHeaderView(createSpacer(height), null, false);
+ listView.addFooterView(createSpacer(height), null, false);
listView.setOnTouchListener((v, ev) -> {
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
@@ -72,11 +77,19 @@ public class KeyguardUserSwitcherPopupMenu extends ListPopupWindow {
}
return false;
});
+ super.show();
}
- @Override
- public void dismiss() {
- getListView().removeOnLayoutChangeListener(mLayoutListener);
- super.dismiss();
+ private View createSpacer(int height) {
+ return new View(mContext) {
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(1, height);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ }
+ };
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index f13730e602a0..3f5c2c8f45d5 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -171,8 +171,8 @@ public final class PhoneStateMonitor {
return mStatusBarStateController.isDozing();
}
- private boolean isLauncherShowing(ActivityManager.RunningTaskInfo runningTaskInfo) {
- if (runningTaskInfo == null) {
+ private boolean isLauncherShowing(@Nullable ActivityManager.RunningTaskInfo runningTaskInfo) {
+ if (runningTaskInfo == null || runningTaskInfo.topActivity == null) {
return false;
} else {
return runningTaskInfo.topActivity.equals(mDefaultHome);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index e35b55841f2f..1496f170dffe 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -636,7 +636,9 @@ public abstract class AuthBiometricView extends LinearLayout {
mIndicatorView.setText(message);
mIndicatorView.setTextColor(mTextColorError);
mIndicatorView.setVisibility(View.VISIBLE);
- mIndicatorView.setSelected(true);
+ // select to enable marquee unless a screen reader is enabled
+ mIndicatorView.setSelected(!mAccessibilityManager.isEnabled()
+ || !mAccessibilityManager.isTouchExplorationEnabled());
mHandler.postDelayed(resetMessageRunnable, mInjector.getDelayAfterError());
Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 6edf2a87f903..1ac9016a1ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -49,7 +49,6 @@ import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseBooleanArray;
@@ -65,6 +64,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.concurrency.Execution;
import java.util.ArrayList;
import java.util.Arrays;
@@ -92,15 +92,20 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
private static final boolean DEBUG = true;
private static final int SENSOR_PRIVACY_DELAY = 500;
- private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Handler mHandler;
+ private final Execution mExecution;
private final CommandQueue mCommandQueue;
private final ActivityTaskManager mActivityTaskManager;
- @Nullable private final FingerprintManager mFingerprintManager;
- @Nullable private final FaceManager mFaceManager;
+ @Nullable
+ private final FingerprintManager mFingerprintManager;
+ @Nullable
+ private final FaceManager mFaceManager;
private final Provider<UdfpsController> mUdfpsControllerFactory;
private final Provider<SidefpsController> mSidefpsControllerFactory;
- @Nullable private final PointF mFaceAuthSensorLocation;
- @Nullable private PointF mFingerprintLocation;
+ @Nullable
+ private final PointF mFaceAuthSensorLocation;
+ @Nullable
+ private PointF mFingerprintLocation;
private final Set<Callback> mCallbacks = new HashSet<>();
// TODO: These should just be saved from onSaveState
@@ -133,62 +138,27 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
}
- private final FingerprintStateListener mFingerprintStateListener =
- new FingerprintStateListener() {
- @Override
- public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
- Log.d(TAG, "onEnrollmentsChanged, userId: " + userId
- + ", sensorId: " + sensorId
- + ", hasEnrollments: " + hasEnrollments);
- for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) {
- if (prop.sensorId == sensorId) {
- mUdfpsEnrolledForUser.put(userId, hasEnrollments);
- }
- }
-
- for (Callback cb : mCallbacks) {
- cb.onEnrollmentsChanged();
- }
- }
- };
-
- @NonNull
private final IFingerprintAuthenticatorsRegisteredCallback
mFingerprintAuthenticatorsRegisteredCallback =
new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
- @Override public void onAllAuthenticatorsRegistered(
+ @Override
+ public void onAllAuthenticatorsRegistered(
List<FingerprintSensorPropertiesInternal> sensors) {
- if (DEBUG) {
- Log.d(TAG, "onFingerprintProvidersAvailable | sensors: " + Arrays.toString(
- sensors.toArray()));
- }
- mFpProps = sensors;
- List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
- List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>();
- for (FingerprintSensorPropertiesInternal props : mFpProps) {
- if (props.isAnyUdfpsType()) {
- udfpsProps.add(props);
- }
- if (props.isAnySidefpsType()) {
- sidefpsProps.add(props);
- }
- }
- mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
- if (mUdfpsProps != null) {
- mUdfpsController = mUdfpsControllerFactory.get();
- }
- mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
- if (mSidefpsProps != null) {
- mSidefpsController = mSidefpsControllerFactory.get();
- }
+ mHandler.post(() -> handleAllAuthenticatorsRegistered(sensors));
+ }
+ };
- for (Callback cb : mCallbacks) {
- cb.onAllAuthenticatorsRegistered();
- }
+ private final FingerprintStateListener mFingerprintStateListener =
+ new FingerprintStateListener() {
+ @Override
+ public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ mHandler.post(
+ () -> handleEnrollmentsChanged(userId, sensorId, hasEnrollments));
}
};
- @VisibleForTesting final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @VisibleForTesting
+ final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mCurrentDialog != null
@@ -212,6 +182,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
};
private void handleTaskStackChanged() {
+ mExecution.assertIsMainThread();
if (mCurrentDialog != null) {
try {
final String clientPackage = mCurrentDialog.getOpPackageName();
@@ -241,6 +212,56 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
}
+ private void handleAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ mExecution.assertIsMainThread();
+ if (DEBUG) {
+ Log.d(TAG, "handleAllAuthenticatorsRegistered | sensors: " + Arrays.toString(
+ sensors.toArray()));
+ }
+ mFpProps = sensors;
+ List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
+ List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>();
+ for (FingerprintSensorPropertiesInternal props : mFpProps) {
+ if (props.isAnyUdfpsType()) {
+ udfpsProps.add(props);
+ }
+ if (props.isAnySidefpsType()) {
+ sidefpsProps.add(props);
+ }
+ }
+ mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
+ if (mUdfpsProps != null) {
+ mUdfpsController = mUdfpsControllerFactory.get();
+ }
+ mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
+ if (mSidefpsProps != null) {
+ mSidefpsController = mSidefpsControllerFactory.get();
+ }
+ for (Callback cb : mCallbacks) {
+ cb.onAllAuthenticatorsRegistered();
+ }
+ mFingerprintManager.registerFingerprintStateListener(mFingerprintStateListener);
+ }
+
+ private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ mExecution.assertIsMainThread();
+ Log.d(TAG, "handleEnrollmentsChanged, userId: " + userId + ", sensorId: " + sensorId
+ + ", hasEnrollments: " + hasEnrollments);
+ if (mUdfpsProps == null) {
+ Log.d(TAG, "handleEnrollmentsChanged, mUdfpsProps is null");
+ } else {
+ for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) {
+ if (prop.sensorId == sensorId) {
+ mUdfpsEnrolledForUser.put(userId, hasEnrollments);
+ }
+ }
+ }
+ for (Callback cb : mCallbacks) {
+ cb.onEnrollmentsChanged();
+ }
+ }
+
/**
* Adds a callback. See {@link Callback}.
*/
@@ -449,6 +470,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@Inject
public AuthController(Context context,
+ Execution execution,
CommandQueue commandQueue,
ActivityTaskManager activityTaskManager,
@NonNull WindowManager windowManager,
@@ -459,6 +481,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@NonNull DisplayManager displayManager,
@Main Handler handler) {
super(context);
+ mExecution = execution;
+ mHandler = handler;
mCommandQueue = commandQueue;
mActivityTaskManager = activityTaskManager;
mFingerprintManager = fingerprintManager;
@@ -470,7 +494,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mOrientationListener = new BiometricDisplayListener(
context,
displayManager,
- handler,
+ mHandler,
BiometricDisplayListener.SensorType.Generic.INSTANCE,
() -> {
onOrientationChanged();
@@ -521,7 +545,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
if (mFingerprintManager != null) {
mFingerprintManager.addAuthenticatorsRegisteredCallback(
mFingerprintAuthenticatorsRegisteredCallback);
- mFingerprintManager.registerFingerprintStateListener(mFingerprintStateListener);
}
mTaskStackListener = new BiometricTaskStackListener();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 90a1e5e64daf..f82ea790bb64 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -80,11 +80,6 @@ class AuthRippleController @Inject constructor(
private var circleReveal: LightRevealEffect? = null
private var udfpsController: UdfpsController? = null
-
- private var dwellScale = 2f
- private var expandedDwellScale = 2.5f
- private var aodDwellScale = 1.9f
- private var aodExpandedDwellScale = 2.3f
private var udfpsRadius: Float = -1f
override fun onInit() {
@@ -128,7 +123,7 @@ class AuthRippleController @Inject constructor(
updateSensorLocation()
if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
fingerprintSensorLocation != null) {
- mView.setSensorLocation(fingerprintSensorLocation!!)
+ mView.setFingerprintSensorLocation(fingerprintSensorLocation!!, udfpsRadius)
showUnlockedRipple()
} else if (biometricSourceType == BiometricSourceType.FACE &&
faceSensorLocation != null) {
@@ -241,24 +236,12 @@ class AuthRippleController @Inject constructor(
}
private fun updateRippleColor() {
- mView.setColor(
- Utils.getColorAttr(sysuiContext, android.R.attr.colorAccent).defaultColor)
+ mView.setLockScreenColor(Utils.getColorAttrDefaultColor(sysuiContext,
+ R.attr.wallpaperTextColorAccent))
}
private fun showDwellRipple() {
- if (statusBarStateController.isDozing) {
- mView.startDwellRipple(
- /* startRadius */ udfpsRadius,
- /* endRadius */ udfpsRadius * aodDwellScale,
- /* expandedRadius */ udfpsRadius * aodExpandedDwellScale,
- /* isDozing */ true)
- } else {
- mView.startDwellRipple(
- /* startRadius */ udfpsRadius,
- /* endRadius */ udfpsRadius * dwellScale,
- /* expandedRadius */ udfpsRadius * expandedDwellScale,
- /* isDozing */ false)
- }
+ mView.startDwellRipple(statusBarStateController.isDozing)
}
private val keyguardUpdateMonitorCallback =
@@ -295,7 +278,7 @@ class AuthRippleController @Inject constructor(
return
}
- mView.setSensorLocation(fingerprintSensorLocation!!)
+ mView.setFingerprintSensorLocation(fingerprintSensorLocation!!, udfpsRadius)
showDwellRipple()
}
@@ -307,8 +290,8 @@ class AuthRippleController @Inject constructor(
private val authControllerCallback =
object : AuthController.Callback {
override fun onAllAuthenticatorsRegistered() {
- updateSensorLocation()
updateUdfpsDependentParams()
+ updateSensorLocation()
}
override fun onEnrollmentsChanged() {
@@ -329,20 +312,6 @@ class AuthRippleController @Inject constructor(
}
inner class AuthRippleCommand : Command {
- fun printLockScreenDwellInfo(pw: PrintWriter) {
- pw.println("lock screen dwell ripple: " +
- "\n\tsensorLocation=$fingerprintSensorLocation" +
- "\n\tdwellScale=$dwellScale" +
- "\n\tdwellExpand=$expandedDwellScale")
- }
-
- fun printAodDwellInfo(pw: PrintWriter) {
- pw.println("aod dwell ripple: " +
- "\n\tsensorLocation=$fingerprintSensorLocation" +
- "\n\tdwellScale=$aodDwellScale" +
- "\n\tdwellExpand=$aodExpandedDwellScale")
- }
-
override fun execute(pw: PrintWriter, args: List<String>) {
if (args.isEmpty()) {
invalidCommand(pw)
@@ -350,11 +319,9 @@ class AuthRippleController @Inject constructor(
when (args[0]) {
"dwell" -> {
showDwellRipple()
- if (statusBarStateController.isDozing) {
- printAodDwellInfo(pw)
- } else {
- printLockScreenDwellInfo(pw)
- }
+ pw.println("lock screen dwell ripple: " +
+ "\n\tsensorLocation=$fingerprintSensorLocation" +
+ "\n\tudfpsRadius=$udfpsRadius")
}
"fingerprint" -> {
updateSensorLocation()
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index c6d26ffb9957..d67363079e17 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -21,6 +21,7 @@ import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
+import android.graphics.Color
import android.graphics.Paint
import android.graphics.PointF
import android.util.AttributeSet
@@ -28,6 +29,7 @@ import android.view.View
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
import com.android.systemui.animation.Interpolators
+import com.android.systemui.statusbar.charging.DwellRippleShader
import com.android.systemui.statusbar.charging.RippleShader
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
@@ -43,23 +45,32 @@ private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private val retractInterpolator = PathInterpolator(.05f, .93f, .1f, 1f)
- private val dwellPulseDuration = 50L
- private val dwellAlphaDuration = dwellPulseDuration
- private val dwellAlpha: Float = 1f
- private val dwellExpandDuration = 1200L - dwellPulseDuration
+ private val dwellPulseDuration = 100L
+ private val dwellExpandDuration = 2000L - dwellPulseDuration
- private val aodDwellPulseDuration = 50L
- private var aodDwellAlphaDuration = aodDwellPulseDuration
- private var aodDwellAlpha: Float = .8f
- private var aodDwellExpandDuration = 1200L - aodDwellPulseDuration
+ private var drawDwell: Boolean = false
+ private var drawRipple: Boolean = false
+ private var lockScreenColorVal = Color.WHITE
private val retractDuration = 400L
private var alphaInDuration: Long = 0
private var unlockedRippleInProgress: Boolean = false
+ private val dwellShader = DwellRippleShader()
+ private val dwellPaint = Paint()
private val rippleShader = RippleShader()
private val ripplePaint = Paint()
private var retractAnimator: Animator? = null
private var dwellPulseOutAnimator: Animator? = null
+ private var dwellRadius: Float = 0f
+ set(value) {
+ dwellShader.maxRadius = value
+ field = value
+ }
+ private var dwellOrigin: PointF = PointF()
+ set(value) {
+ dwellShader.origin = value
+ field = value
+ }
private var radius: Float = 0f
set(value) {
rippleShader.radius = value
@@ -76,6 +87,11 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
rippleShader.progress = 0f
rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
ripplePaint.shader = rippleShader
+
+ dwellShader.color = 0xffffffff.toInt() // default color
+ dwellShader.progress = 0f
+ dwellShader.distortionStrength = .4f
+ dwellPaint.shader = dwellShader
visibility = GONE
}
@@ -84,6 +100,13 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
radius = maxOf(location.x, location.y, width - location.x, height - location.y).toFloat()
}
+ fun setFingerprintSensorLocation(location: PointF, sensorRadius: Float) {
+ origin = location
+ radius = maxOf(location.x, location.y, width - location.x, height - location.y).toFloat()
+ dwellOrigin = location
+ dwellRadius = sensorRadius * 1.5f
+ }
+
fun setAlphaInDuration(duration: Long) {
alphaInDuration = duration
}
@@ -97,14 +120,14 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
}
if (dwellPulseOutAnimator?.isRunning == true) {
- val retractRippleAnimator = ValueAnimator.ofFloat(rippleShader.progress, 0f)
+ val retractRippleAnimator = ValueAnimator.ofFloat(dwellShader.progress, 0f)
.apply {
interpolator = retractInterpolator
duration = retractDuration
addUpdateListener { animator ->
val now = animator.currentPlayTime
- rippleShader.progress = animator.animatedValue as Float
- rippleShader.time = now.toFloat()
+ dwellShader.progress = animator.animatedValue as Float
+ dwellShader.time = now.toFloat()
invalidate()
}
@@ -114,8 +137,8 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
interpolator = Interpolators.LINEAR
duration = retractDuration
addUpdateListener { animator ->
- rippleShader.color = ColorUtils.setAlphaComponent(
- rippleShader.color,
+ dwellShader.color = ColorUtils.setAlphaComponent(
+ dwellShader.color,
animator.animatedValue as Int
)
invalidate()
@@ -127,13 +150,12 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
dwellPulseOutAnimator?.cancel()
- rippleShader.shouldFadeOutRipple = false
- visibility = VISIBLE
+ drawDwell = true
}
override fun onAnimationEnd(animation: Animator?) {
- visibility = GONE
- resetRippleAlpha()
+ drawDwell = false
+ resetDwellAlpha()
}
})
start()
@@ -142,101 +164,54 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
}
/**
- * Ripple that moves animates from an outer ripple ring of
- * startRadius => endRadius => expandedRadius
+ * Plays a ripple animation that grows to the dwellRadius with distortion.
*/
- fun startDwellRipple(
- startRadius: Float,
- endRadius: Float,
- expandedRadius: Float,
- isDozing: Boolean
- ) {
+ fun startDwellRipple(isDozing: Boolean) {
if (unlockedRippleInProgress || dwellPulseOutAnimator?.isRunning == true) {
return
}
- // we divide by 4 because the desired startRadius and endRadius is for the ripple's outer
- // ring see RippleShader
- val startDwellProgress = startRadius / radius / 4f
- val endInitialDwellProgress = endRadius / radius / 4f
- val endExpandDwellProgress = expandedRadius / radius / 4f
-
- val alpha = if (isDozing) aodDwellAlpha else dwellAlpha
- val pulseOutEndAlpha = (255 * alpha).toInt()
- val expandDwellEndAlpha = kotlin.math.min((255 * (alpha + .25f)).toInt(), 255)
- val dwellPulseOutRippleAnimator = ValueAnimator.ofFloat(startDwellProgress,
- endInitialDwellProgress).apply {
- interpolator = Interpolators.LINEAR_OUT_SLOW_IN
- duration = if (isDozing) aodDwellPulseDuration else dwellPulseDuration
- addUpdateListener { animator ->
- val now = animator.currentPlayTime
- rippleShader.progress = animator.animatedValue as Float
- rippleShader.time = now.toFloat()
-
- invalidate()
- }
- }
+ updateDwellRippleColor(isDozing)
- val dwellPulseOutAlphaAnimator = ValueAnimator.ofInt(0, pulseOutEndAlpha).apply {
+ val dwellPulseOutRippleAnimator = ValueAnimator.ofFloat(0f, .8f).apply {
interpolator = Interpolators.LINEAR
- duration = if (isDozing) aodDwellAlphaDuration else dwellAlphaDuration
+ duration = dwellPulseDuration
addUpdateListener { animator ->
- rippleShader.color = ColorUtils.setAlphaComponent(
- rippleShader.color,
- animator.animatedValue as Int
- )
+ val now = animator.currentPlayTime
+ dwellShader.progress = animator.animatedValue as Float
+ dwellShader.time = now.toFloat()
+
invalidate()
}
}
// slowly animate outwards until we receive a call to retractRipple or startUnlockedRipple
- val expandDwellRippleAnimator = ValueAnimator.ofFloat(endInitialDwellProgress,
- endExpandDwellProgress).apply {
+ val expandDwellRippleAnimator = ValueAnimator.ofFloat(.8f, 1f).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
- duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
+ duration = dwellExpandDuration
addUpdateListener { animator ->
val now = animator.currentPlayTime
- rippleShader.progress = animator.animatedValue as Float
- rippleShader.time = now.toFloat()
+ dwellShader.progress = animator.animatedValue as Float
+ dwellShader.time = now.toFloat()
invalidate()
}
}
- val expandDwellAlphaAnimator = ValueAnimator.ofInt(pulseOutEndAlpha, expandDwellEndAlpha)
- .apply {
- interpolator = Interpolators.LINEAR
- duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
- addUpdateListener { animator ->
- rippleShader.color = ColorUtils.setAlphaComponent(
- rippleShader.color,
- animator.animatedValue as Int
- )
- invalidate()
- }
- }
-
- val initialDwellPulseOutAnimator = AnimatorSet().apply {
- playTogether(dwellPulseOutRippleAnimator, dwellPulseOutAlphaAnimator)
- }
- val expandDwellAnimator = AnimatorSet().apply {
- playTogether(expandDwellRippleAnimator, expandDwellAlphaAnimator)
- }
-
dwellPulseOutAnimator = AnimatorSet().apply {
playSequentially(
- initialDwellPulseOutAnimator,
- expandDwellAnimator
+ dwellPulseOutRippleAnimator,
+ expandDwellRippleAnimator
)
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
retractAnimator?.cancel()
- rippleShader.shouldFadeOutRipple = false
visibility = VISIBLE
+ drawDwell = true
}
override fun onAnimationEnd(animation: Animator?) {
- visibility = GONE
+ drawDwell = false
resetRippleAlpha()
}
})
@@ -252,16 +227,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
return // Ignore if ripple effect is already playing
}
- var rippleStart = 0f
- var alphaDuration = alphaInDuration
- if (dwellPulseOutAnimator?.isRunning == true || retractAnimator?.isRunning == true) {
- rippleStart = rippleShader.progress
- alphaDuration = 0
- dwellPulseOutAnimator?.cancel()
- retractAnimator?.cancel()
- }
-
- val rippleAnimator = ValueAnimator.ofFloat(rippleStart, 1f).apply {
+ val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
duration = AuthRippleController.RIPPLE_ANIMATION_DURATION
addUpdateListener { animator ->
@@ -274,7 +240,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
}
val alphaInAnimator = ValueAnimator.ofInt(0, 255).apply {
- duration = alphaDuration
+ duration = alphaInDuration
addUpdateListener { animator ->
rippleShader.color = ColorUtils.setAlphaComponent(
rippleShader.color,
@@ -293,12 +259,14 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
override fun onAnimationStart(animation: Animator?) {
unlockedRippleInProgress = true
rippleShader.shouldFadeOutRipple = true
+ drawRipple = true
visibility = VISIBLE
}
override fun onAnimationEnd(animation: Animator?) {
onAnimationEnd?.run()
unlockedRippleInProgress = false
+ drawRipple = false
visibility = GONE
}
})
@@ -313,17 +281,42 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
)
}
- fun setColor(color: Int) {
- rippleShader.color = color
+ fun setLockScreenColor(color: Int) {
+ lockScreenColorVal = color
+ rippleShader.color = lockScreenColorVal
resetRippleAlpha()
}
+ fun updateDwellRippleColor(isDozing: Boolean) {
+ if (isDozing) {
+ dwellShader.color = Color.WHITE
+ } else {
+ dwellShader.color = lockScreenColorVal
+ }
+ resetDwellAlpha()
+ }
+
+ fun resetDwellAlpha() {
+ dwellShader.color = ColorUtils.setAlphaComponent(
+ dwellShader.color,
+ 255
+ )
+ }
+
override fun onDraw(canvas: Canvas?) {
// To reduce overdraw, we mask the effect to a circle whose radius is big enough to cover
// the active effect area. Values here should be kept in sync with the
// animation implementation in the ripple shader.
- val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
- (1 - rippleShader.progress)) * radius * 2f
- canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
+ if (drawDwell) {
+ val maskRadius = (1 - (1 - dwellShader.progress) * (1 - dwellShader.progress) *
+ (1 - dwellShader.progress)) * dwellRadius * 2f
+ canvas?.drawCircle(dwellOrigin.x, dwellOrigin.y, maskRadius, dwellPaint)
+ }
+
+ if (drawRipple) {
+ val mask = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * radius * 2f
+ canvas?.drawCircle(origin.x, origin.y, mask, ripplePaint)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index 07aec6994bd0..73e3aecd8b63 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -75,7 +75,7 @@ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
@Override
protected void onViewDetached() {
mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener);
- mDialogManager.registerListener(mDialogListener);
+ mDialogManager.unregisterListener(mDialogListener);
mDumpManger.unregisterDumpable(getDumpTag());
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
index 13b1dc034a19..f965431a8001 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
@@ -142,7 +142,7 @@ public class CommunalSourcePrimer extends CoreStartable {
private void connect() {
if (DEBUG) {
- Log.d(TAG, "attempting to communal to communal source");
+ Log.d(TAG, "attempting to connect to communal source");
}
if (mCurrentConnection != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 34261487cadc..9dddbb1d67c6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -25,7 +25,7 @@ import com.android.systemui.accessibility.WindowMagnification;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.communal.CommunalManagerUpdater;
import com.android.systemui.dreams.DreamOverlayRegistrant;
-import com.android.systemui.dreams.appwidgets.AppWidgetOverlayPrimer;
+import com.android.systemui.dreams.appwidgets.ComplicationPrimer;
import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.dagger.KeyguardModule;
@@ -202,9 +202,9 @@ public abstract class SystemUIBinder {
/** Inject into AppWidgetOverlayPrimer. */
@Binds
@IntoMap
- @ClassKey(AppWidgetOverlayPrimer.class)
+ @ClassKey(ComplicationPrimer.class)
public abstract CoreStartable bindAppWidgetOverlayPrimer(
- AppWidgetOverlayPrimer appWidgetOverlayPrimer);
+ ComplicationPrimer complicationPrimer);
/** Inject into CommunalManagerUpdater. */
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 9c25b3596be6..2beed4c6a7e7 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -40,7 +40,6 @@ public interface DozeHost {
void extendPulse(int reason);
void setAnimateWakeup(boolean animateWakeup);
- void setAnimateScreenOff(boolean animateScreenOff);
/**
* Reports that a tap event happend on the Sensors Low Power Island.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index b2fe3bb94dd3..e568b8282856 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -21,30 +21,21 @@ import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
import android.app.AlarmManager;
import android.content.Context;
-import android.content.res.Configuration;
import android.os.Handler;
import android.os.SystemClock;
-import android.provider.Settings;
import android.text.format.Formatter;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.unfold.FoldAodAnimationController;
-import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus;
-import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.WakeLock;
import java.util.Calendar;
-import java.util.Optional;
import javax.inject.Inject;
@@ -52,9 +43,7 @@ import javax.inject.Inject;
* The policy controlling doze.
*/
@DozeScope
-public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
- ConfigurationController.ConfigurationListener, FoldAodAnimationStatus,
- StatusBarStateController.StateListener {
+public class DozeUi implements DozeMachine.Part {
// if enabled, calls dozeTimeTick() whenever the time changes:
private static final boolean BURN_IN_TESTING_ENABLED = false;
private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
@@ -62,26 +51,15 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
private final DozeHost mHost;
private final Handler mHandler;
private final WakeLock mWakeLock;
- private final FoldAodAnimationController mFoldAodAnimationController;
private DozeMachine mMachine;
private final AlarmTimeout mTimeTicker;
private final boolean mCanAnimateTransition;
private final DozeParameters mDozeParameters;
private final DozeLog mDozeLog;
private final StatusBarStateController mStatusBarStateController;
- private final TunerService mTunerService;
- private final ConfigurationController mConfigurationController;
-
- private boolean mKeyguardShowing;
private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
new KeyguardUpdateMonitorCallback() {
@Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- mKeyguardShowing = showing;
- updateAnimateScreenOff();
- }
-
- @Override
public void onTimeChanged() {
if (BURN_IN_TESTING_ENABLED && mStatusBarStateController.isDozing()) {
// update whenever the time changes for manual burn in testing
@@ -91,11 +69,6 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
mHandler.post(mWakeLock.wrap(() -> {}));
}
}
-
- @Override
- public void onShadeExpandedChanged(boolean expanded) {
- updateAnimateScreenOff();
- }
};
private long mLastTimeTickElapsed = 0;
@@ -104,10 +77,8 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
public DozeUi(Context context, AlarmManager alarmManager,
WakeLock wakeLock, DozeHost host, @Main Handler handler,
DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
- DozeLog dozeLog, TunerService tunerService,
StatusBarStateController statusBarStateController,
- Optional<SysUIUnfoldComponent> sysUiUnfoldComponent,
- ConfigurationController configurationController) {
+ DozeLog dozeLog) {
mContext = context;
mWakeLock = wakeLock;
mHost = host;
@@ -117,31 +88,7 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
mDozeLog = dozeLog;
- mTunerService = tunerService;
mStatusBarStateController = statusBarStateController;
- mStatusBarStateController.addCallback(this);
-
- mTunerService.addTunable(this, Settings.Secure.DOZE_ALWAYS_ON);
-
- mConfigurationController = configurationController;
- mConfigurationController.addCallback(this);
-
- mFoldAodAnimationController = sysUiUnfoldComponent
- .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
-
- if (mFoldAodAnimationController != null) {
- mFoldAodAnimationController.addCallback(this);
- }
- }
-
- @Override
- public void destroy() {
- mTunerService.removeTunable(this);
- mConfigurationController.removeCallback(this);
-
- if (mFoldAodAnimationController != null) {
- mFoldAodAnimationController.removeCallback(this);
- }
}
@Override
@@ -149,22 +96,6 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
mMachine = dozeMachine;
}
- /**
- * Decide if we're taking over the screen-off animation
- * when the device was configured to skip doze after screen off.
- */
- private void updateAnimateScreenOff() {
- if (mCanAnimateTransition) {
- final boolean controlScreenOff =
- mDozeParameters.getAlwaysOn()
- && (mKeyguardShowing || mDozeParameters.shouldControlUnlockedScreenOff())
- && !mHost.isPowerSaveActive();
- mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
- mHost.setAnimateScreenOff(controlScreenOff
- && mDozeParameters.shouldAnimateDozingChange());
- }
- }
-
private void pulseWhileDozing(int reason) {
mHost.pulseWhileDozing(
new DozeHost.PulseCallback() {
@@ -293,34 +224,4 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
scheduleTimeTick();
}
-
- @VisibleForTesting
- KeyguardUpdateMonitorCallback getKeyguardCallback() {
- return mKeyguardVisibilityCallback;
- }
-
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (key.equals(Settings.Secure.DOZE_ALWAYS_ON)) {
- updateAnimateScreenOff();
- }
- }
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- updateAnimateScreenOff();
- }
-
- /**
- * Called when StatusBar state changed, could affect unlocked screen off animation state
- */
- @Override
- public void onStatePostChange() {
- updateAnimateScreenOff();
- }
-
- @Override
- public void onFoldToAodAnimationChanged() {
- updateAnimateScreenOff();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/OverlayHost.java b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationHost.java
index 08f0f3507e3e..7c3152fe89dc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/OverlayHost.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationHost.java
@@ -19,29 +19,29 @@ package com.android.systemui.dreams;
import android.view.View;
/**
- * A collection of interfaces related to hosting an overlay.
+ * A collection of interfaces related to hosting a complication.
*/
-public abstract class OverlayHost {
+public abstract class ComplicationHost {
/**
- * An interface for the callback from the overlay provider to indicate when the overlay is
- * ready.
+ * An interface for the callback from the complication provider to indicate when the
+ * complication is ready.
*/
public interface CreationCallback {
/**
- * Called to inform the overlay view is ready to be placed within the visual space.
- * @param view The view representing the overlay.
+ * Called to inform the complication view is ready to be placed within the visual space.
+ * @param view The view representing the complication.
* @param layoutParams The parameters to create the view with.
*/
- void onCreated(View view, OverlayHostView.LayoutParams layoutParams);
+ void onCreated(View view, ComplicationHostView.LayoutParams layoutParams);
}
/**
- * An interface for the callback from the overlay provider to signal interactions in the
- * overlay.
+ * An interface for the callback from the complication provider to signal interactions in the
+ * complication.
*/
public interface InteractionCallback {
/**
- * Called to signal the calling overlay would like to exit the dream.
+ * Called to signal the calling complication would like to exit the dream.
*/
void onExit();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/OverlayHostView.java b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationHostView.java
index 7870426c78f1..a67dd5c0a723 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/OverlayHostView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationHostView.java
@@ -22,22 +22,23 @@ import android.util.AttributeSet;
import androidx.constraintlayout.widget.ConstraintLayout;
/**
- * {@link OverlayHostView} is the container view for housing overlays ontop of a dream.
+ * {@link ComplicationHostView} is the container view for housing complications above of a dream.
*/
-public class OverlayHostView extends ConstraintLayout {
- public OverlayHostView(Context context) {
+public class ComplicationHostView extends ConstraintLayout {
+ public ComplicationHostView(Context context) {
super(context, null);
}
- public OverlayHostView(Context context, AttributeSet attrs) {
+ public ComplicationHostView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}
- public OverlayHostView(Context context, AttributeSet attrs, int defStyleAttr) {
+ public ComplicationHostView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr, 0);
}
- public OverlayHostView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public ComplicationHostView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/OverlayProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java
index f20802527d73..099e37960ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/OverlayProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java
@@ -19,18 +19,18 @@ package com.android.systemui.dreams;
import android.content.Context;
/**
- * {@link OverlayProvider} is an interface for defining entities that can supply overlays to show
- * over a dream. Presentation components such as the {@link DreamOverlayService} supply
+ * {@link ComplicationProvider} is an interface for defining entities that can supply complications
+ * to show over a dream. Presentation components such as the {@link DreamOverlayService} supply
* implementations with the necessary context for constructing such overlays.
*/
-public interface OverlayProvider {
+public interface ComplicationProvider {
/**
- * Called when the {@link OverlayHost} requests the associated overlay be produced.
+ * Called when the {@link ComplicationHost} requests the associated complication be produced.
*
* @param context The {@link Context} used to construct the view.
- * @param creationCallback The callback to inform when the overlay has been created.
- * @param interactionCallback The callback to inform when the overlay has been interacted with.
+ * @param creationCallback The callback to inform the complication has been created.
+ * @param interactionCallback The callback to inform the complication has been interacted with.
*/
- void onCreateOverlay(Context context, OverlayHost.CreationCallback creationCallback,
- OverlayHost.InteractionCallback interactionCallback);
+ void onCreateComplication(Context context, ComplicationHost.CreationCallback creationCallback,
+ ComplicationHost.InteractionCallback interactionCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 393f039d91ea..7c2bb4bcf4f3 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -40,7 +40,7 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
/**
- * The {@link DreamOverlayService} is responsible for placing overlays on top of a dream. The
+ * The {@link DreamOverlayService} is responsible for placing an overlay on top of a dream. The
* dream reaches directly out to the service with a Window reference (via LayoutParams), which the
* service uses to insert its own child Window into the dream's parent Window.
*/
@@ -52,7 +52,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private final Context mContext;
// The Executor ensures actions and ui updates happen on the same thread.
private final Executor mExecutor;
- // The state controller informs the service of updates to the overlays present.
+ // The state controller informs the service of updates to the complications present.
private final DreamOverlayStateController mStateController;
// The component used to resolve dream overlay dependencies.
private final DreamOverlayComponent mDreamOverlayComponent;
@@ -64,8 +64,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private final DreamOverlayStateController.Callback mOverlayStateCallback =
new DreamOverlayStateController.Callback() {
@Override
- public void onOverlayChanged() {
- mExecutor.execute(() -> reloadOverlaysLocked());
+ public void onComplicationsChanged() {
+ mExecutor.execute(() -> reloadComplicationsLocked());
}
};
@@ -87,7 +87,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
}
};
- // A hook into the internal inset calculation where we declare the overlays as the only
+ // A hook into the internal inset calculation where we declare the complications as the only
// touchable regions.
private final ViewTreeObserver.OnComputeInternalInsetsListener
mOnComputeInternalInsetsListener =
@@ -129,20 +129,20 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mExecutor.execute(() -> addOverlayWindowLocked(layoutParams));
}
- private void reloadOverlaysLocked() {
+ private void reloadComplicationsLocked() {
if (mDreamOverlayContentView == null) {
return;
}
mDreamOverlayContentView.removeAllViews();
- for (OverlayProvider overlayProvider : mStateController.getOverlays()) {
- addOverlay(overlayProvider);
+ for (ComplicationProvider complicationProvider : mStateController.getComplications()) {
+ addComplication(complicationProvider);
}
}
/**
- * Inserts {@link Window} to host dream overlays into the dream's parent window. Must be called
- * from the main executing thread. The window attributes closely mirror those that are set by
- * the {@link android.service.dreams.DreamService} on the dream Window.
+ * Inserts {@link Window} to host the dream overlay into the dream's parent window. Must be
+ * called from the main executing thread. The window attributes closely mirror those that are
+ * set by the {@link android.service.dreams.DreamService} on the dream Window.
* @param layoutParams The {@link android.view.WindowManager.LayoutParams} which allow inserting
* into the dream window.
*/
@@ -173,12 +173,12 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
windowManager.addView(window.getDecorView(), window.getAttributes());
- mExecutor.execute(this::reloadOverlaysLocked);
+ mExecutor.execute(this::reloadComplicationsLocked);
}
@VisibleForTesting
- protected void addOverlay(OverlayProvider provider) {
- provider.onCreateOverlay(mContext,
+ protected void addComplication(ComplicationProvider provider) {
+ provider.onCreateComplication(mContext,
(view, layoutParams) -> {
// Always move UI related work to the main thread.
mExecutor.execute(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index d248a9e174f5..66679bb4ee44 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -17,45 +17,51 @@
package com.android.systemui.dreams;
import androidx.annotation.NonNull;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.policy.CallbackController;
+import com.google.common.util.concurrent.ListenableFuture;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Objects;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
/**
- * {@link DreamOverlayStateController} is the source of truth for Dream overlay configurations.
- * Clients can register as listeners for changes to the overlay composition and can query for the
- * overlays on-demand.
+ * {@link DreamOverlayStateController} is the source of truth for Dream overlay configurations and
+ * state. Clients can register as listeners for changes to the overlay composition and can query for
+ * the complications on-demand.
*/
@SysUISingleton
public class DreamOverlayStateController implements
CallbackController<DreamOverlayStateController.Callback> {
- // A counter for guaranteeing unique overlay tokens within the scope of this state controller.
- private int mNextOverlayTokenId = 0;
+ // A counter for guaranteeing unique complications tokens within the scope of this state
+ // controller.
+ private int mNextComplicationTokenId = 0;
/**
- * {@link OverlayToken} provides a unique key for identifying {@link OverlayProvider}
+ * {@link ComplicationToken} provides a unique key for identifying {@link ComplicationProvider}
* instances registered with {@link DreamOverlayStateController}.
*/
- public static class OverlayToken {
+ public static class ComplicationToken {
private final int mId;
- private OverlayToken(int id) {
+ private ComplicationToken(int id) {
mId = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (!(o instanceof OverlayToken)) return false;
- OverlayToken that = (OverlayToken) o;
+ if (!(o instanceof ComplicationToken)) return false;
+ ComplicationToken that = (ComplicationToken) o;
return mId == that.mId;
}
@@ -70,81 +76,97 @@ public class DreamOverlayStateController implements
*/
public interface Callback {
/**
- * Called when the visibility of the communal view changes.
+ * Called when the composition of complications changes.
*/
- default void onOverlayChanged() {
+ default void onComplicationsChanged() {
}
}
+ private final Executor mExecutor;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
- private final HashMap<OverlayToken, OverlayProvider> mOverlays = new HashMap<>();
+ private final HashMap<ComplicationToken, ComplicationProvider> mComplications = new HashMap<>();
@VisibleForTesting
@Inject
- public DreamOverlayStateController() {
+ public DreamOverlayStateController(@Main Executor executor) {
+ mExecutor = executor;
}
/**
- * Adds an overlay to be presented on top of dreams.
- * @param provider The {@link OverlayProvider} providing the dream.
- * @return The {@link OverlayToken} tied to the supplied {@link OverlayProvider}.
+ * Adds a complication to be presented on top of dreams.
+ * @param provider The {@link ComplicationProvider} providing the dream.
+ * @return The {@link ComplicationToken} tied to the supplied {@link ComplicationProvider}.
*/
- public OverlayToken addOverlay(OverlayProvider provider) {
- final OverlayToken token = new OverlayToken(mNextOverlayTokenId++);
- mOverlays.put(token, provider);
- notifyCallbacks();
- return token;
+ public ListenableFuture<ComplicationToken> addComplication(ComplicationProvider provider) {
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ mExecutor.execute(() -> {
+ final ComplicationToken token = new ComplicationToken(mNextComplicationTokenId++);
+ mComplications.put(token, provider);
+ notifyCallbacks();
+ completer.set(token);
+ });
+ return "DreamOverlayStateController::addComplication";
+ });
}
/**
- * Removes an overlay from being shown on dreams.
- * @param token The {@link OverlayToken} associated with the {@link OverlayProvider} to be
- * removed.
- * @return The removed {@link OverlayProvider}, {@code null} if not found.
+ * Removes a complication from being shown on dreams.
+ * @param token The {@link ComplicationToken} associated with the {@link ComplicationProvider}
+ * to be removed.
+ * @return The removed {@link ComplicationProvider}, {@code null} if not found.
*/
- public OverlayProvider removeOverlay(OverlayToken token) {
- final OverlayProvider removedOverlay = mOverlays.remove(token);
-
- if (removedOverlay != null) {
- notifyCallbacks();
- }
-
- return removedOverlay;
+ public ListenableFuture<ComplicationProvider> removeComplication(ComplicationToken token) {
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ mExecutor.execute(() -> {
+ final ComplicationProvider removedComplication = mComplications.remove(token);
+
+ if (removedComplication != null) {
+ notifyCallbacks();
+ }
+ completer.set(removedComplication);
+ });
+
+ return "DreamOverlayStateController::removeComplication";
+ });
}
private void notifyCallbacks() {
for (Callback callback : mCallbacks) {
- callback.onOverlayChanged();
+ callback.onComplicationsChanged();
}
}
@Override
public void addCallback(@NonNull Callback callback) {
- Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
- if (mCallbacks.contains(callback)) {
- return;
- }
+ mExecutor.execute(() -> {
+ Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
+ if (mCallbacks.contains(callback)) {
+ return;
+ }
- mCallbacks.add(callback);
+ mCallbacks.add(callback);
- if (mOverlays.isEmpty()) {
- return;
- }
+ if (mComplications.isEmpty()) {
+ return;
+ }
- callback.onOverlayChanged();
+ callback.onComplicationsChanged();
+ });
}
@Override
public void removeCallback(@NonNull Callback callback) {
- Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
- mCallbacks.remove(callback);
+ mExecutor.execute(() -> {
+ Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
+ mCallbacks.remove(callback);
+ });
}
/**
- * Returns all registered {@link OverlayProvider} instances.
- * @return A collection of {@link OverlayProvider}.
+ * Returns all registered {@link ComplicationProvider} instances.
+ * @return A collection of {@link ComplicationProvider}.
*/
- public Collection<OverlayProvider> getOverlays() {
- return mOverlays.values();
+ public Collection<ComplicationProvider> getComplications() {
+ return mComplications.values();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java
index d1da1e691ed6..687f7a296b1a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java
@@ -34,8 +34,8 @@ import javax.inject.Inject;
/**
* {@link AppWidgetProvider} is a singleton for accessing app widgets within SystemUI. This
- * consolidates resources such as the App Widget Host across potentially multiple
- * {@link AppWidgetOverlayProvider} instances and other usages.
+ * consolidates resources such as the {@link AppWidgetHost} across potentially multiple
+ * {@link ComplicationProvider} instances and other usages.
*/
@SysUISingleton
public class AppWidgetProvider {
@@ -87,8 +87,8 @@ public class AppWidgetProvider {
final float density = mResources.getDisplayMetrics().density;
final int height = Math.round((bottom - top) / density);
final int width = Math.round((right - left) / density);
- appWidgetView.updateAppWidgetSize(null, width, height, width,
- height);
+ appWidgetView.updateAppWidgetSize(null, width, height,
+ width, height);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationPrimer.java
index 563f70776209..7d30fafda8a6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationPrimer.java
@@ -26,25 +26,25 @@ import androidx.constraintlayout.widget.ConstraintSet;
import com.android.systemui.CoreStartable;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.ComplicationHostView;
import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.dreams.OverlayHostView;
-import com.android.systemui.dreams.dagger.AppWidgetOverlayComponent;
+import com.android.systemui.dreams.appwidgets.dagger.AppWidgetComponent;
import javax.inject.Inject;
/**
- * {@link AppWidgetOverlayPrimer} reads the configured App Widget Overlay from resources on start
+ * {@link ComplicationPrimer} reads the configured AppWidget Complications from resources on start
* and populates them into the {@link DreamOverlayStateController}.
*/
-public class AppWidgetOverlayPrimer extends CoreStartable {
+public class ComplicationPrimer extends CoreStartable {
private final Resources mResources;
private final DreamOverlayStateController mDreamOverlayStateController;
- private final AppWidgetOverlayComponent.Factory mComponentFactory;
+ private final AppWidgetComponent.Factory mComponentFactory;
@Inject
- public AppWidgetOverlayPrimer(Context context, @Main Resources resources,
+ public ComplicationPrimer(Context context, @Main Resources resources,
DreamOverlayStateController overlayStateController,
- AppWidgetOverlayComponent.Factory appWidgetOverlayFactory) {
+ AppWidgetComponent.Factory appWidgetOverlayFactory) {
super(context);
mResources = resources;
mDreamOverlayStateController = overlayStateController;
@@ -62,17 +62,18 @@ public class AppWidgetOverlayPrimer extends CoreStartable {
}
/**
- * Generates the {@link OverlayHostView.LayoutParams} for a given gravity. Default dimension
- * constraints are also included in the params.
+ * Generates the {@link ComplicationHostView.LayoutParams} for a given gravity. Default
+ * dimension constraints are also included in the params.
* @param gravity The gravity for the layout as defined by {@link Gravity}.
* @param resources The resourcs from which default dimensions will be extracted from.
- * @return {@link OverlayHostView.LayoutParams} representing the provided gravity and default
- * parameters.
+ * @return {@link ComplicationHostView.LayoutParams} representing the provided gravity and
+ * default parameters.
*/
- private static OverlayHostView.LayoutParams getLayoutParams(int gravity, Resources resources) {
- final OverlayHostView.LayoutParams params = new OverlayHostView.LayoutParams(
- OverlayHostView.LayoutParams.MATCH_CONSTRAINT,
- OverlayHostView.LayoutParams.MATCH_CONSTRAINT);
+ private static ComplicationHostView.LayoutParams getLayoutParams(int gravity,
+ Resources resources) {
+ final ComplicationHostView.LayoutParams params = new ComplicationHostView.LayoutParams(
+ ComplicationHostView.LayoutParams.MATCH_CONSTRAINT,
+ ComplicationHostView.LayoutParams.MATCH_CONSTRAINT);
if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
params.bottomToBottom = ConstraintSet.PARENT_ID;
@@ -92,28 +93,28 @@ public class AppWidgetOverlayPrimer extends CoreStartable {
// For now, apply the same sizing constraints on every widget.
params.matchConstraintPercentHeight =
- resources.getFloat(R.dimen.config_dreamOverlayComponentHeightPercent);
+ resources.getFloat(R.dimen.config_dreamComplicationHeightPercent);
params.matchConstraintPercentWidth =
- resources.getFloat(R.dimen.config_dreamOverlayComponentWidthPercent);
+ resources.getFloat(R.dimen.config_dreamComplicationWidthPercent);
return params;
}
-
/**
* Helper method for loading widgets based on configuration.
*/
private void loadDefaultWidgets() {
- final int[] positions = mResources.getIntArray(R.array.config_dreamOverlayPositions);
+ final int[] positions = mResources.getIntArray(R.array.config_dreamComplicationPositions);
final String[] components =
- mResources.getStringArray(R.array.config_dreamOverlayComponents);
+ mResources.getStringArray(R.array.config_dreamAppWidgetComplications);
for (int i = 0; i < Math.min(positions.length, components.length); i++) {
- final AppWidgetOverlayComponent component = mComponentFactory.build(
+ final AppWidgetComponent component = mComponentFactory.build(
ComponentName.unflattenFromString(components[i]),
getLayoutParams(positions[i], mResources));
- mDreamOverlayStateController.addOverlay(component.getAppWidgetOverlayProvider());
+ mDreamOverlayStateController.addComplication(
+ component.getAppWidgetComplicationProvider());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationProvider.java
index a635d3f740cf..9188ee54d855 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationProvider.java
@@ -22,30 +22,30 @@ import android.content.Context;
import android.util.Log;
import android.widget.RemoteViews;
-import com.android.systemui.dreams.OverlayHost;
-import com.android.systemui.dreams.OverlayHostView;
-import com.android.systemui.dreams.OverlayProvider;
+import com.android.systemui.dreams.ComplicationHost;
+import com.android.systemui.dreams.ComplicationHostView;
import com.android.systemui.plugins.ActivityStarter;
import javax.inject.Inject;
/**
- * {@link AppWidgetOverlayProvider} is an implementation of {@link OverlayProvider} for providing
- * app widget-based overlays.
+ * {@link ComplicationProvider} is an implementation of
+ * {@link com.android.systemui.dreams.ComplicationProvider} for providing app widget-based
+ * complications.
*/
-public class AppWidgetOverlayProvider implements OverlayProvider {
- private static final String TAG = "AppWdgtOverlayProvider";
+public class ComplicationProvider implements com.android.systemui.dreams.ComplicationProvider {
+ private static final String TAG = "AppWidgetCompProvider";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final ActivityStarter mActivityStarter;
private final AppWidgetProvider mAppWidgetProvider;
private final ComponentName mComponentName;
- private final OverlayHostView.LayoutParams mLayoutParams;
+ private final ComplicationHostView.LayoutParams mLayoutParams;
@Inject
- public AppWidgetOverlayProvider(ActivityStarter activityStarter,
+ public ComplicationProvider(ActivityStarter activityStarter,
ComponentName componentName, AppWidgetProvider widgetProvider,
- OverlayHostView.LayoutParams layoutParams) {
+ ComplicationHostView.LayoutParams layoutParams) {
mActivityStarter = activityStarter;
mComponentName = componentName;
mAppWidgetProvider = widgetProvider;
@@ -53,8 +53,9 @@ public class AppWidgetOverlayProvider implements OverlayProvider {
}
@Override
- public void onCreateOverlay(Context context, OverlayHost.CreationCallback creationCallback,
- OverlayHost.InteractionCallback interactionCallback) {
+ public void onCreateComplication(Context context,
+ ComplicationHost.CreationCallback creationCallback,
+ ComplicationHost.InteractionCallback interactionCallback) {
final AppWidgetHostView widget = mAppWidgetProvider.getWidget(mComponentName);
if (widget == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/AppWidgetOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/dagger/AppWidgetComponent.java
index 3103057be209..7beed176eeca 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/AppWidgetOverlayComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/dagger/AppWidgetComponent.java
@@ -14,26 +14,26 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.dagger;
+package com.android.systemui.dreams.appwidgets.dagger;
import android.content.ComponentName;
-import com.android.systemui.dreams.OverlayHostView;
-import com.android.systemui.dreams.appwidgets.AppWidgetOverlayProvider;
+import com.android.systemui.dreams.ComplicationHostView;
+import com.android.systemui.dreams.appwidgets.ComplicationProvider;
import dagger.BindsInstance;
import dagger.Subcomponent;
/** */
@Subcomponent
-public interface AppWidgetOverlayComponent {
+public interface AppWidgetComponent {
/** */
@Subcomponent.Factory
interface Factory {
- AppWidgetOverlayComponent build(@BindsInstance ComponentName component,
- @BindsInstance OverlayHostView.LayoutParams layoutParams);
+ AppWidgetComponent build(@BindsInstance ComponentName component,
+ @BindsInstance ComplicationHostView.LayoutParams layoutParams);
}
- /** Builds a {@link AppWidgetOverlayProvider}. */
- AppWidgetOverlayProvider getAppWidgetOverlayProvider();
+ /** Builds a {@link ComplicationProvider}. */
+ ComplicationProvider getAppWidgetComplicationProvider();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index ff5beb54bd89..0d4688ec880c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -16,11 +16,15 @@
package com.android.systemui.dreams.dagger;
+import com.android.systemui.dreams.appwidgets.dagger.AppWidgetComponent;
+
import dagger.Module;
/**
* Dagger Module providing Communal-related functionality.
*/
-@Module(subcomponents = {AppWidgetOverlayComponent.class, DreamOverlayComponent.class})
+@Module(subcomponents = {
+ AppWidgetComponent.class,
+ DreamOverlayComponent.class})
public interface DreamModule {
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index d9f66631b973..4e852a871417 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -98,9 +98,6 @@ public class Flags {
/***************************************/
// 600- status bar
- public static final BooleanFlag STATUS_BAR_PROVIDER_MODEL =
- new BooleanFlag(600, false);
-
public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
new BooleanFlag(601, false);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index a801647ba7d2..69bcf2ec8b8d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -24,6 +24,7 @@ import android.graphics.Matrix
import android.view.RemoteAnimationTarget
import android.view.SyncRtSurfaceTransactionApplier
import android.view.View
+import androidx.annotation.VisibleForTesting
import androidx.core.math.MathUtils
import com.android.internal.R
import com.android.keyguard.KeyguardClockSwitchController
@@ -33,6 +34,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
+import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
@@ -93,7 +95,8 @@ class KeyguardUnlockAnimationController @Inject constructor(
keyguardViewMediator: Lazy<KeyguardViewMediator>,
private val keyguardViewController: KeyguardViewController,
private val smartspaceTransitionController: SmartspaceTransitionController,
- private val featureFlags: FeatureFlags
+ private val featureFlags: FeatureFlags,
+ private val biometricUnlockController: BiometricUnlockController
) : KeyguardStateController.Callback {
/**
@@ -107,7 +110,8 @@ class KeyguardUnlockAnimationController @Inject constructor(
* If we're unlocking via biometrics, PIN entry, or from clicking a notification, a canned
* animation is started in [notifyStartKeyguardExitAnimation].
*/
- private var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
+ @VisibleForTesting
+ var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
private var surfaceBehindRemoteAnimationTarget: RemoteAnimationTarget? = null
private var surfaceBehindRemoteAnimationStartTime: Long = 0
@@ -134,7 +138,8 @@ class KeyguardUnlockAnimationController @Inject constructor(
* Animator that animates in the surface behind the keyguard. This is used to play a canned
* animation on the surface, if we're not doing a swipe gesture.
*/
- private val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f)
+ @VisibleForTesting
+ val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f)
/** Rounded corner radius to apply to the surface behind the keyguard. */
private var roundedCornerRadius = 0f
@@ -222,7 +227,18 @@ class KeyguardUnlockAnimationController @Inject constructor(
// to animate it in. Otherwise, the swipe touch events will continue animating it.
if (!requestedShowSurfaceBehindKeyguard) {
keyguardViewController.hide(startTime, 350)
- surfaceBehindEntryAnimator.start()
+
+ // If we're wake and unlocking, we don't want to animate the surface since we're going
+ // to do the light reveal scrim from the black AOD screen. Make it visible and end the
+ // remote aimation.
+ if (biometricUnlockController.isWakeAndUnlock) {
+ setSurfaceBehindAppearAmount(1f)
+ keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ } else {
+ // Otherwise, animate it in normally.
+ surfaceBehindEntryAnimator.start()
+ }
}
// Finish the keyguard remote animation if the dismiss amount has crossed the threshold.
@@ -266,7 +282,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
* animations and swipe gestures to animate the surface's entry (and exit, if the swipe is
* cancelled).
*/
- private fun setSurfaceBehindAppearAmount(amount: Float) {
+ fun setSurfaceBehindAppearAmount(amount: Float) {
if (surfaceBehindRemoteAnimationTarget == null) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 90d56244fb7c..0512d488bef9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2107,15 +2107,6 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private final Runnable mKeyguardGoingAwayRunnable = new Runnable() {
@Override
public void run() {
- // If the keyguard is already going away, or it's about to because we are going to
- // trigger the going-away remote animation to show the surface behind, don't do it
- // again. That will cause the current animation to be cancelled unnecessarily.
- if (mKeyguardStateController.isKeyguardGoingAway()
- || mSurfaceBehindRemoteAnimationRequested
- || mSurfaceBehindRemoteAnimationRunning) {
- return;
- }
-
Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");
if (DEBUG) Log.d(TAG, "keyguardGoingAway");
mKeyguardViewControllerLazy.get().keyguardGoingAway();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt
index bccd106db836..a60033cf40fe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt
@@ -25,7 +25,7 @@ import java.util.concurrent.atomic.AtomicReference
*/
class PendingDrawnTasksContainer {
- private lateinit var pendingDrawnTasksCount: AtomicInteger
+ private var pendingDrawnTasksCount: AtomicInteger = AtomicInteger(0)
private var completionCallback: AtomicReference<Runnable> = AtomicReference()
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index fb601e310702..c8cd43287c99 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.annotation.IntDef
import android.content.Context
+import android.content.res.Configuration
import android.graphics.Rect
import android.util.MathUtils
import android.view.View
@@ -41,6 +42,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.Utils
import com.android.systemui.util.animation.UniqueObjectHostView
import javax.inject.Inject
@@ -186,6 +188,8 @@ class MediaHierarchyManager @Inject constructor(
@MediaLocation
private var currentAttachmentLocation = -1
+ private var inSplitShade = false
+
/**
* Is there any active media in the carousel?
*/
@@ -390,8 +394,9 @@ class MediaHierarchyManager @Inject constructor(
init {
updateConfiguration()
configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
- override fun onDensityOrFontScaleChanged() {
+ override fun onConfigChanged(newConfig: Configuration?) {
updateConfiguration()
+ updateDesiredLocation(forceNoAnimation = true, forceStateUpdate = true)
}
})
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
@@ -467,6 +472,7 @@ class MediaHierarchyManager @Inject constructor(
private fun updateConfiguration() {
distanceForFullShadeTransition = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_media_transition_distance)
+ inSplitShade = Utils.shouldUseSplitNotificationShade(context.resources)
}
/**
@@ -803,7 +809,7 @@ class MediaHierarchyManager @Inject constructor(
private fun getQSTransformationProgress(): Float {
val currentHost = getHost(desiredLocation)
val previousHost = getHost(previousLocation)
- if (hasActiveMedia && currentHost?.location == LOCATION_QS) {
+ if (hasActiveMedia && (currentHost?.location == LOCATION_QS && !inSplitShade)) {
if (previousHost?.location == LOCATION_QQS) {
if (previousHost.visible || statusbarState != StatusBarState.KEYGUARD) {
return qsExpansion
@@ -934,7 +940,7 @@ class MediaHierarchyManager @Inject constructor(
statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications()
val location = when {
- qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS
+ (qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
!hasActiveMedia -> LOCATION_QS
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index 042a337322e9..1322adeb85a8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -26,6 +27,8 @@ import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.util.animation.TransitionLayout
+private const val TAG = "PlayerViewHolder"
+
/**
* ViewHolder for a media player.
*/
@@ -94,7 +97,12 @@ class PlayerViewHolder private constructor(itemView: View) {
}
fun marquee(start: Boolean, delay: Long) {
- longPressText.getHandler().postDelayed({ longPressText.setSelected(start) }, delay)
+ val longPressTextHandler = longPressText.getHandler()
+ if (longPressTextHandler == null) {
+ Log.d(TAG, "marquee while longPressText.getHandler() is null", Exception())
+ return
+ }
+ longPressTextHandler.postDelayed({ longPressText.setSelected(start) }, delay)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index a1f8455f9ac8..a398a7fa4575 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -20,6 +20,8 @@ import android.content.Context;
import android.view.WindowManager;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
@@ -28,8 +30,10 @@ import com.android.systemui.media.taptotransfer.MediaTttChipController;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
import com.android.systemui.statusbar.commandline.CommandRegistry;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.Optional;
+import java.util.concurrent.Executor;
import javax.inject.Named;
@@ -79,11 +83,14 @@ public interface MediaModule {
static Optional<MediaTttChipController> providesMediaTttChipController(
MediaTttFlags mediaTttFlags,
Context context,
- WindowManager windowManager) {
+ WindowManager windowManager,
+ @Main Executor mainExecutor,
+ @Background Executor backgroundExecutor) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttChipController(context, windowManager));
+ return Optional.of(new MediaTttChipController(
+ context, windowManager, mainExecutor, backgroundExecutor));
}
/** */
@@ -92,10 +99,13 @@ public interface MediaModule {
static Optional<MediaTttCommandLineHelper> providesMediaTttCommandLineHelper(
MediaTttFlags mediaTttFlags,
CommandRegistry commandRegistry,
- MediaTttChipController mediaTttChipController) {
+ Context context,
+ MediaTttChipController mediaTttChipController,
+ @Main DelayableExecutor mainExecutor) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttCommandLineHelper(commandRegistry, mediaTttChipController));
+ return Optional.of(new MediaTttCommandLineHelper(
+ commandRegistry, context, mediaTttChipController, mainExecutor));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 8b19ccb04f16..e465ae41f546 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -66,18 +66,6 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
if (position == size && mController.isZeroMode()) {
viewHolder.onBind(CUSTOMIZED_ITEM_PAIR_NEW, false /* topMargin */,
true /* bottomMargin */);
- } else if (mIncludeDynamicGroup) {
- if (position == 0) {
- viewHolder.onBind(CUSTOMIZED_ITEM_DYNAMIC_GROUP, true /* topMargin */,
- false /* bottomMargin */);
- } else {
- // When group item is added at the first(position == 0), devices will be added from
- // the second item(position == 1). It means that the index of device list starts
- // from "position - 1".
- viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices()))
- .get(position - 1),
- false /* topMargin */, position == size /* bottomMargin */, position);
- }
} else if (position < size) {
viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position),
position == 0 /* topMargin */, position == (size - 1) /* bottomMargin */,
@@ -89,11 +77,6 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
@Override
public int getItemCount() {
- mIncludeDynamicGroup = mController.getSelectedMediaDevice().size() > 1;
- if (mController.isZeroMode() || mIncludeDynamicGroup) {
- // Add extra one for "pair new" or dynamic group
- return mController.getMediaDevices().size() + 1;
- }
return mController.getMediaDevices().size();
}
@@ -115,16 +98,6 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mStatusIcon.setVisibility(View.GONE);
mTitleText.setTextColor(Utils.getColorStateListDefaultColor(mContext,
R.color.media_dialog_inactive_item_main_content));
- if (currentlyConnected && mController.isActiveRemoteDevice(device)
- && mController.getSelectableMediaDevice().size() > 0) {
- // Init active device layout
- mAddIcon.setVisibility(View.VISIBLE);
- mAddIcon.setTransitionAlpha(1);
- mAddIcon.setOnClickListener(this::onEndItemClick);
- } else {
- // Init non-active device layout
- mAddIcon.setVisibility(View.GONE);
- }
if (mCurrentActivePosition == position) {
mCurrentActivePosition = -1;
}
@@ -158,6 +131,19 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
true /* showSubtitle */, true /* showStatus */);
mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
+ } else if (mController.getSelectedMediaDevice().size() > 1
+ && isDeviceIncluded(mController.getSelectedMediaDevice(), device)) {
+ mTitleText.setTextColor(Utils.getColorStateListDefaultColor(mContext,
+ R.color.media_dialog_active_item_main_content));
+ setSingleLineLayout(getItemTitle(device), true /* bFocused */,
+ true /* showSeekBar */,
+ false /* showProgressBar */, false /* showStatus */);
+ mCheckBox.setVisibility(View.VISIBLE);
+ mCheckBox.setChecked(true);
+ mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ onCheckBoxClicked(false, device);
+ });
+ initSessionSeekbar();
} else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) {
mStatusIcon.setImageDrawable(
mContext.getDrawable(R.drawable.media_output_status_check));
@@ -168,6 +154,16 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
false /* showProgressBar */, true /* showStatus */);
initSeekbar(device);
mCurrentActivePosition = position;
+ } else if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
+ mCheckBox.setVisibility(View.VISIBLE);
+ mCheckBox.setChecked(false);
+ mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ onCheckBoxClicked(true, device);
+ });
+ setSingleLineLayout(getItemTitle(device), false /* bFocused */,
+ false /* showSeekBar */,
+ false /* showProgressBar */, false /* showStatus */);
+ mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
} else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */);
mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
@@ -181,7 +177,6 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mTitleText.setTextColor(Utils.getColorStateListDefaultColor(mContext,
R.color.media_dialog_inactive_item_main_content));
mCheckBox.setVisibility(View.GONE);
- mAddIcon.setVisibility(View.GONE);
setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
false /* bFocused */);
final Drawable d = mContext.getDrawable(R.drawable.ic_add);
@@ -189,26 +184,25 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN));
mTitleIcon.setImageDrawable(d);
mContainerLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
- } else if (customizedItem == CUSTOMIZED_ITEM_DYNAMIC_GROUP) {
- mTitleText.setTextColor(Utils.getColorStateListDefaultColor(mContext,
- R.color.media_dialog_active_item_main_content));
- mConnectedItem = mContainerLayout;
- mCheckBox.setVisibility(View.GONE);
- if (mController.getSelectableMediaDevice().size() > 0) {
- mAddIcon.setVisibility(View.VISIBLE);
- mAddIcon.setTransitionAlpha(1);
- mAddIcon.setOnClickListener(this::onEndItemClick);
- } else {
- mAddIcon.setVisibility(View.GONE);
+ }
+ }
+
+ private void onCheckBoxClicked(boolean isChecked, MediaDevice device) {
+ if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
+ mController.addDeviceToPlayMedia(device);
+ } else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
+ device)) {
+ mController.removeDeviceFromPlayMedia(device);
+ }
+ }
+
+ private boolean isDeviceIncluded(List<MediaDevice> deviceList, MediaDevice targetDevice) {
+ for (MediaDevice device : deviceList) {
+ if (TextUtils.equals(device.getId(), targetDevice.getId())) {
+ return true;
}
- mTitleIcon.setImageDrawable(getSpeakerDrawable());
- final CharSequence sessionName = mController.getSessionName();
- final CharSequence title = TextUtils.isEmpty(sessionName)
- ? mContext.getString(R.string.media_output_dialog_group) : sessionName;
- setTwoLineLayout(title, true /* bFocused */, true /* showSeekBar */,
- false /* showProgressBar */, false /* showSubtitle */);
- initSessionSeekbar();
}
+ return false;
}
private void onItemClick(View view, MediaDevice device) {
@@ -229,9 +223,5 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mController.launchBluetoothPairing();
}
}
-
- private void onEndItemClick(View view) {
- mController.launchMediaOutputGroupDialog(mMediaOutputDialog.getDialogView());
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index bff792c99837..a8d30d485141 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -118,7 +118,6 @@ public abstract class MediaOutputBaseAdapter extends
final TextView mTwoLineTitleText;
final TextView mSubTitleText;
final ImageView mTitleIcon;
- final ImageView mAddIcon;
final ProgressBar mProgressBar;
final SeekBar mSeekBar;
final RelativeLayout mTwoLineLayout;
@@ -137,7 +136,6 @@ public abstract class MediaOutputBaseAdapter extends
mTitleIcon = view.requireViewById(R.id.title_icon);
mProgressBar = view.requireViewById(R.id.volume_indeterminate_progress);
mSeekBar = view.requireViewById(R.id.volume_seekbar);
- mAddIcon = view.requireViewById(R.id.add_icon);
mStatusIcon = view.requireViewById(R.id.media_output_item_status);
mCheckBox = view.requireViewById(R.id.check_box);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 4eee60c75bfd..83d581fb7897 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -258,16 +258,15 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
drawable = mContext.getDrawable(com.android.internal.R.drawable.ic_bt_headphones_a2dp);
}
if (!(drawable instanceof BitmapDrawable)) {
- setColorFilter(drawable,
- mLocalMediaManager.getCurrentConnectedDevice().getId().equals(device.getId()));
+ setColorFilter(drawable, isActiveItem(device));
}
return BluetoothUtils.createIconWithDrawable(drawable);
}
- void setColorFilter(Drawable drawable, boolean isConnected) {
+ void setColorFilter(Drawable drawable, boolean isActive) {
final ColorStateList list =
mContext.getResources().getColorStateList(
- !hasAdjustVolumeUserRestriction() && isConnected && !isTransferring()
+ isActive
? R.color.media_dialog_active_item_main_content
: R.color.media_dialog_inactive_item_main_content,
mContext.getTheme());
@@ -275,6 +274,15 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
PorterDuff.Mode.SRC_IN));
}
+ boolean isActiveItem(MediaDevice device) {
+ boolean isConnected = mLocalMediaManager.getCurrentConnectedDevice().getId().equals(
+ device.getId());
+ boolean isSelectedDeviceInGroup = getSelectedMediaDevice().size() > 1
+ && getSelectedMediaDevice().contains(device);
+ return (!hasAdjustVolumeUserRestriction() && isConnected && !isTransferring())
+ || isSelectedDeviceInGroup;
+ }
+
IconCompat getNotificationIcon() {
if (TextUtils.isEmpty(mPackageName)) {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
index 104ddf907dc7..9b42b1dea9a4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
@@ -35,6 +35,7 @@ import java.util.List;
/**
* Adapter for media output dynamic group dialog.
*/
+//TODO: clear this class after new UI updated
public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter {
private static final String TAG = "MediaOutputGroupAdapter";
@@ -96,7 +97,6 @@ public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter {
@Override
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
super.onBind(device, topMargin, bottomMargin, position);
- mAddIcon.setVisibility(View.GONE);
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
onCheckBoxClicked(isChecked, device);
@@ -131,7 +131,6 @@ public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter {
false /* showSubtitle*/);
mTitleIcon.setImageDrawable(getSpeakerDrawable());
mCheckBox.setVisibility(View.GONE);
- mAddIcon.setVisibility(View.GONE);
initSessionSeekbar();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
index 376fea28cf82..2b55d634b382 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
@@ -25,8 +25,13 @@ import android.view.View
import android.view.WindowManager
import android.widget.LinearLayout
import android.widget.TextView
+import com.android.internal.widget.CachingIconView
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
const val TAG = "MediaTapToTransfer"
@@ -41,6 +46,8 @@ const val TAG = "MediaTapToTransfer"
class MediaTttChipController @Inject constructor(
private val context: Context,
private val windowManager: WindowManager,
+ @Main private val mainExecutor: Executor,
+ @Background private val backgroundExecutor: Executor,
) {
@SuppressLint("WrongConstant") // We're allowed to use TYPE_VOLUME_OVERLAY
@@ -68,6 +75,11 @@ class MediaTttChipController @Inject constructor(
}
val currentChipView = chipView!!
+ // App icon
+ currentChipView.findViewById<CachingIconView>(R.id.app_icon).apply {
+ this.setImageDrawable(chipState.appIconDrawable)
+ }
+
// Text
currentChipView.requireViewById<TextView>(R.id.text).apply {
text = context.getString(chipState.chipText, chipState.otherDeviceName)
@@ -92,6 +104,11 @@ class MediaTttChipController @Inject constructor(
}
undoView.setOnClickListener(undoClickListener)
+ // Future handling
+ if (chipState is TransferInitiated) {
+ addFutureCallback(chipState)
+ }
+
// Add view if necessary
if (oldChipView == null) {
windowManager.addView(chipView, windowLayoutParams)
@@ -104,4 +121,33 @@ class MediaTttChipController @Inject constructor(
windowManager.removeView(chipView)
chipView = null
}
+
+ /**
+ * Adds the appropriate callbacks to [chipState.future] so that we update the chip correctly
+ * when the future resolves.
+ */
+ private fun addFutureCallback(chipState: TransferInitiated) {
+ // Listen to the future on a background thread so we don't occupy the main thread while we
+ // wait for it to complete.
+ backgroundExecutor.execute {
+ try {
+ val undoRunnable = chipState.future.get(TRANSFER_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+ // Make UI changes on the main thread
+ mainExecutor.execute {
+ displayChip(
+ TransferSucceeded(
+ chipState.otherDeviceName, chipState.appIconDrawable, undoRunnable
+ )
+ )
+ }
+ } catch (ex: Exception) {
+ // TODO(b/203800327): Maybe show a failure chip here if UX decides we need one.
+ mainExecutor.execute {
+ removeChip()
+ }
+ }
+ }
+ }
}
+
+private const val TRANSFER_TIMEOUT_SECONDS = 10L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
index 3b3adfd2297b..62e3b1ac8d5c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
@@ -16,8 +16,10 @@
package com.android.systemui.media.taptotransfer
+import android.graphics.drawable.Drawable
import androidx.annotation.StringRes
import com.android.systemui.R
+import java.util.concurrent.Future
/**
* A class that stores all the information necessary to display the media tap-to-transfer chip in
@@ -25,12 +27,15 @@ import com.android.systemui.R
*
* This is a sealed class where each subclass represents a specific chip state. Each subclass can
* contain additional information that is necessary for only that state.
+ *
+ * @property chipText a string resource for the text that the chip should display.
+ * @property otherDeviceName the name of the other device involved in the transfer.
+ * @property appIconDrawable a drawable representing the icon of the app playing the media.
*/
sealed class MediaTttChipState(
- /** A string resource for the text that the chip should display. */
@StringRes internal val chipText: Int,
- /** The name of the other device involved in the transfer. */
- internal val otherDeviceName: String
+ internal val otherDeviceName: String,
+ internal val appIconDrawable: Drawable,
)
/**
@@ -38,15 +43,23 @@ sealed class MediaTttChipState(
* The chip will instruct the user to move closer in order to initiate the transfer.
*/
class MoveCloserToTransfer(
- otherDeviceName: String
-) : MediaTttChipState(R.string.media_move_closer_to_transfer, otherDeviceName)
+ otherDeviceName: String,
+ appIconDrawable: Drawable
+) : MediaTttChipState(R.string.media_move_closer_to_transfer, otherDeviceName, appIconDrawable)
/**
* A state representing that a transfer has been initiated (but not completed).
+ *
+ * @property future a future that will be resolved when the transfer has either succeeded or failed.
+ * If the transfer succeeded, the future can optionally return an undo runnable (see
+ * [TransferSucceeded.undoRunnable]). [MediaTttChipController] is responsible for transitioning
+ * the chip to the [TransferSucceeded] state if the future resolves successfully.
*/
class TransferInitiated(
- otherDeviceName: String
-) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName)
+ otherDeviceName: String,
+ appIconDrawable: Drawable,
+ val future: Future<Runnable?>
+) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName, appIconDrawable)
/**
* A state representing that a transfer has been successfully completed.
@@ -56,5 +69,6 @@ class TransferInitiated(
*/
class TransferSucceeded(
otherDeviceName: String,
+ appIconDrawable: Drawable,
val undoRunnable: Runnable? = null
-) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName)
+) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName, appIconDrawable)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 6a02dabb9813..74983e5bfe03 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -16,12 +16,18 @@
package com.android.systemui.media.taptotransfer
+import android.content.Context
+import android.graphics.drawable.Icon
import android.util.Log
import androidx.annotation.VisibleForTesting
+import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.PrintWriter
+import java.util.concurrent.FutureTask
import javax.inject.Inject
/**
@@ -31,8 +37,13 @@ import javax.inject.Inject
@SysUISingleton
class MediaTttCommandLineHelper @Inject constructor(
commandRegistry: CommandRegistry,
- private val mediaTttChipController: MediaTttChipController
+ context: Context,
+ private val mediaTttChipController: MediaTttChipController,
+ @Main private val mainExecutor: DelayableExecutor,
) {
+ private val appIconDrawable =
+ Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
+
init {
commandRegistry.registerCommand(ADD_CHIP_COMMAND_TAG) { AddChipCommand() }
commandRegistry.registerCommand(REMOVE_CHIP_COMMAND_TAG) { RemoveChipCommand() }
@@ -43,14 +54,21 @@ class MediaTttCommandLineHelper @Inject constructor(
val otherDeviceName = args[0]
when (args[1]) {
MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> {
- mediaTttChipController.displayChip(MoveCloserToTransfer(otherDeviceName))
+ mediaTttChipController.displayChip(
+ MoveCloserToTransfer(otherDeviceName, appIconDrawable)
+ )
}
TRANSFER_INITIATED_COMMAND_NAME -> {
- mediaTttChipController.displayChip(TransferInitiated(otherDeviceName))
+ val futureTask = FutureTask { fakeUndoRunnable }
+ mediaTttChipController.displayChip(
+ TransferInitiated(otherDeviceName, appIconDrawable, futureTask)
+ )
+ mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME)
+
}
TRANSFER_SUCCEEDED_COMMAND_NAME -> {
mediaTttChipController.displayChip(
- TransferSucceeded(otherDeviceName, fakeUndoRunnable)
+ TransferSucceeded(otherDeviceName, appIconDrawable, fakeUndoRunnable)
)
}
else -> {
@@ -94,3 +112,5 @@ val MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME = MoveCloserToTransfer::class.simpleNam
val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!!
@VisibleForTesting
val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
+
+private const val FUTURE_WAIT_TIME = 2000L
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index c648e9b092d2..1030c21ecc79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -39,7 +39,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
@@ -53,7 +52,6 @@ import com.android.systemui.qs.external.TileServices;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.connectivity.StatusBarFlags;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -98,7 +96,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private final UiEventLogger mUiEventLogger;
private final InstanceIdSequence mInstanceIdSequence;
private final CustomTileStatePersister mCustomTileStatePersister;
- private final FeatureFlags mFeatureFlags;
private final List<Callback> mCallbacks = new ArrayList<>();
private AutoTileManager mAutoTiles;
@@ -111,7 +108,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private SecureSettings mSecureSettings;
private final TileServiceRequestController mTileServiceRequestController;
- private final StatusBarFlags mStatusBarFlags;
@Inject
public QSTileHost(Context context,
@@ -130,9 +126,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
UserTracker userTracker,
SecureSettings secureSettings,
CustomTileStatePersister customTileStatePersister,
- TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
- FeatureFlags featureFlags,
- StatusBarFlags statusBarFlags
+ TileServiceRequestController.Builder tileServiceRequestControllerBuilder
) {
mIconController = iconController;
mContext = context;
@@ -144,7 +138,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mUiEventLogger = uiEventLogger;
mBroadcastDispatcher = broadcastDispatcher;
mTileServiceRequestController = tileServiceRequestControllerBuilder.create(this);
- mStatusBarFlags = statusBarFlags;
mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID);
mServices = new TileServices(this, bgLooper, mBroadcastDispatcher, userTracker);
@@ -156,7 +149,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mUserTracker = userTracker;
mSecureSettings = secureSettings;
mCustomTileStatePersister = customTileStatePersister;
- mFeatureFlags = featureFlags;
mainHandler.post(() -> {
// This is technically a hack to avoid circular dependency of
@@ -280,7 +272,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
}
- final List<String> tileSpecs = loadTileSpecs(mContext, newValue, mStatusBarFlags);
+ final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
int currentUser = mUserTracker.getUserId();
if (currentUser != mCurrentUser) {
mUserContext = mUserTracker.getUserContext();
@@ -349,7 +341,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
// If we didn't manage to create any tiles, set it to empty (default)
Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
- changeTiles(currentSpecs, loadTileSpecs(mContext, "", mStatusBarFlags));
+ changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
} else {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onTilesChanged();
@@ -417,7 +409,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private void changeTileSpecs(Predicate<List<String>> changeFunction) {
final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
- final List<String> tileSpecs = loadTileSpecs(mContext, setting, mStatusBarFlags);
+ final List<String> tileSpecs = loadTileSpecs(mContext, setting);
if (changeFunction.test(tileSpecs)) {
saveTilesToSettings(tileSpecs);
}
@@ -506,8 +498,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
}
- protected static List<String> loadTileSpecs(
- Context context, String tileList, StatusBarFlags statusBarFlags) {
+ protected static List<String> loadTileSpecs(Context context, String tileList) {
final Resources res = context.getResources();
if (TextUtils.isEmpty(tileList)) {
@@ -540,20 +531,19 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
}
}
}
- if (statusBarFlags.isProviderModelSettingEnabled()) {
- if (!tiles.contains("internet")) {
- if (tiles.contains("wifi")) {
- // Replace the WiFi with Internet, and remove the Cell
- tiles.set(tiles.indexOf("wifi"), "internet");
- tiles.remove("cell");
- } else if (tiles.contains("cell")) {
- // Replace the Cell with Internet
- tiles.set(tiles.indexOf("cell"), "internet");
- }
- } else {
- tiles.remove("wifi");
+
+ if (!tiles.contains("internet")) {
+ if (tiles.contains("wifi")) {
+ // Replace the WiFi with Internet, and remove the Cell
+ tiles.set(tiles.indexOf("wifi"), "internet");
tiles.remove("cell");
+ } else if (tiles.contains("cell")) {
+ // Replace the Cell with Internet
+ tiles.set(tiles.indexOf("cell"), "internet");
}
+ } else {
+ tiles.remove("wifi");
+ tiles.remove("cell");
}
return tiles;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 6c072f13cf8f..d4350f16873b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -41,7 +41,6 @@ import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.connectivity.StatusBarFlags;
import com.android.systemui.util.leak.GarbageMonitor;
import java.util.ArrayList;
@@ -63,7 +62,6 @@ public class TileQueryHelper {
private final Executor mBgExecutor;
private final Context mContext;
private final UserTracker mUserTracker;
- private final StatusBarFlags mStatusBarFlags;
private TileStateListener mListener;
private boolean mFinished;
@@ -73,14 +71,12 @@ public class TileQueryHelper {
Context context,
UserTracker userTracker,
@Main Executor mainExecutor,
- @Background Executor bgExecutor,
- StatusBarFlags statusBarFlags
+ @Background Executor bgExecutor
) {
mContext = context;
mMainExecutor = mainExecutor;
mBgExecutor = bgExecutor;
mUserTracker = userTracker;
- mStatusBarFlags = statusBarFlags;
}
public void setListener(TileStateListener listener) {
@@ -121,10 +117,8 @@ public class TileQueryHelper {
}
final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
- if (mStatusBarFlags.isProviderModelSettingEnabled()) {
- possibleTiles.remove("cell");
- possibleTiles.remove("wifi");
- }
+ possibleTiles.remove("cell");
+ possibleTiles.remove("wifi");
for (String spec : possibleTiles) {
// Only add current and stock tiles that can be created from QSFactoryImpl.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 11ecac27e88e..e7982bfb7bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -41,6 +41,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
+import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -126,8 +127,8 @@ public class InternetDialog extends SystemUIDialog implements
private Switch mMobileDataToggle;
private View mMobileToggleDivider;
private Switch mWiFiToggle;
- private FrameLayout mDoneLayout;
- private FrameLayout mAirplaneModeLayout;
+ private Button mDoneButton;
+ private Button mAirplaneModeButton;
private Drawable mBackgroundOn;
private Drawable mBackgroundOff = null;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -217,8 +218,8 @@ public class InternetDialog extends SystemUIDialog implements
mWifiSettingsIcon = mDialogView.requireViewById(R.id.wifi_settings_icon);
mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
- mDoneLayout = mDialogView.requireViewById(R.id.done_layout);
- mAirplaneModeLayout = mDialogView.requireViewById(R.id.apm_layout);
+ mDoneButton = mDialogView.requireViewById(R.id.done_button);
+ mAirplaneModeButton = mDialogView.requireViewById(R.id.apm_button);
mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
@@ -240,7 +241,7 @@ public class InternetDialog extends SystemUIDialog implements
setOnClickListener();
mTurnWifiOnLayout.setBackground(null);
- mAirplaneModeLayout.setVisibility(
+ mAirplaneModeButton.setVisibility(
mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
mWifiRecyclerView.setAdapter(mAdapter);
@@ -280,8 +281,8 @@ public class InternetDialog extends SystemUIDialog implements
mConnectedWifListLayout.setOnClickListener(null);
mSeeAllLayout.setOnClickListener(null);
mWiFiToggle.setOnCheckedChangeListener(null);
- mDoneLayout.setOnClickListener(null);
- mAirplaneModeLayout.setOnClickListener(null);
+ mDoneButton.setOnClickListener(null);
+ mAirplaneModeButton.setOnClickListener(null);
mInternetDialogController.onStop();
mInternetDialogFactory.destroyDialog();
}
@@ -307,7 +308,7 @@ public class InternetDialog extends SystemUIDialog implements
}
mInternetDialogTitle.setText(getDialogTitleText());
mInternetDialogSubTitle.setText(getSubtitleText());
- mAirplaneModeLayout.setVisibility(
+ mAirplaneModeButton.setVisibility(
mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
updateEthernet();
@@ -356,8 +357,8 @@ public class InternetDialog extends SystemUIDialog implements
buttonView.setChecked(isChecked);
mWifiManager.setWifiEnabled(isChecked);
});
- mDoneLayout.setOnClickListener(v -> dismiss());
- mAirplaneModeLayout.setOnClickListener(v -> {
+ mDoneButton.setOnClickListener(v -> dismiss());
+ mAirplaneModeButton.setOnClickListener(v -> {
mInternetDialogController.setAirplaneModeDisabled();
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java
deleted file mode 100644
index 6aaba997faad..000000000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.android.systemui.qs.tiles.dialog;
-
-import android.content.Context;
-import android.util.FeatureFlagUtils;
-
-public class InternetDialogUtil {
-
- public static boolean isProviderModelEnabled(Context context) {
- if (context == null) {
- return false;
- }
- return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 6d78b9f37c74..ce571e54e65c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -39,11 +39,13 @@ import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ExitTransitionCoordinator;
import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks;
+import android.app.ICompatCameraControlCallback;
import android.app.Notification;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.PixelFormat;
@@ -72,6 +74,7 @@ import android.view.RemoteAnimationTarget;
import android.view.ScrollCaptureResponse;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsets;
@@ -595,20 +598,35 @@ public class ScreenshotController {
withWindowAttached(() -> {
requestScrollCapture();
mWindow.peekDecorView().getViewRootImpl().setActivityConfigCallback(
- (overrideConfig, newDisplayId) -> {
- if (mConfigChanges.applyNewConfig(mContext.getResources())) {
- // Hide the scroll chip until we know it's available in this orientation
- mScreenshotView.hideScrollChip();
- // Delay scroll capture eval a bit to allow the underlying activity
- // to set up in the new orientation.
- mScreenshotHandler.postDelayed(this::requestScrollCapture, 150);
- mScreenshotView.updateInsets(
- mWindowManager.getCurrentWindowMetrics().getWindowInsets());
- // screenshot animation calculations won't be valid anymore, so just end
- if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
- mScreenshotAnimation.end();
+ new ViewRootImpl.ActivityConfigCallback() {
+ @Override
+ public void onConfigurationChanged(Configuration overrideConfig,
+ int newDisplayId) {
+ if (mConfigChanges.applyNewConfig(mContext.getResources())) {
+ // Hide the scroll chip until we know it's available in this
+ // orientation
+ mScreenshotView.hideScrollChip();
+ // Delay scroll capture eval a bit to allow the underlying activity
+ // to set up in the new orientation.
+ mScreenshotHandler.postDelayed(
+ ScreenshotController.this::requestScrollCapture, 150);
+ mScreenshotView.updateInsets(
+ mWindowManager.getCurrentWindowMetrics()
+ .getWindowInsets());
+ // Screenshot animation calculations won't be valid anymore,
+ // so just end
+ if (mScreenshotAnimation != null
+ && mScreenshotAnimation.isRunning()) {
+ mScreenshotAnimation.end();
+ }
}
}
+ @Override
+ public void requestCompatCameraControl(boolean showControl,
+ boolean transformationApplied,
+ ICompatCameraControlCallback callback) {
+ Log.w(TAG, "Unexpected requestCompatCameraControl callback");
+ }
});
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 0fb08e403483..491a1750a93e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -298,8 +298,8 @@ class LockscreenShadeTransitionController @Inject constructor(
nsslController.setTransitionToFullShadeAmount(field)
notificationPanelController.setTransitionToFullShadeAmount(field,
false /* animate */, 0 /* delay */)
- val progress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
- qS.setTransitionToFullShadeAmount(field, progress)
+ dragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
+ qS.setTransitionToFullShadeAmount(field, dragProgress)
// TODO: appear media also in split shade
val mediaAmount = if (useSplitShade) 0f else field
mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount)
@@ -308,6 +308,9 @@ class LockscreenShadeTransitionController @Inject constructor(
}
}
+ var dragProgress = 0f
+ private set
+
private fun transitionToShadeAmountCommon(dragDownAmount: Float) {
val scrimProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
scrimController.setTransitionToFullShadeProgress(scrimProgress)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
index a1d086b5d768..73d3e2afac7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
@@ -59,8 +59,8 @@ class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) {
}
vec2 distort(vec2 p, float time, float distort_amount_xy, float frequency) {
- return p + vec2(sin(p.x * frequency + in_phase1),
- cos(p.y * frequency * 1.23 + in_phase2)) * distort_amount_xy;
+ return p + vec2(sin(p.y * frequency + in_phase1),
+ cos(p.x * frequency * -1.23 + in_phase2)) * distort_amount_xy;
}
vec4 ripple(vec2 p, float distort_xy, float frequency) {
@@ -73,11 +73,11 @@ class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) {
"""
private const val SHADER_MAIN = """vec4 main(vec2 p) {
vec4 color1 = ripple(p,
- 12 * in_distortion_strength, // distort_xy
+ 34 * in_distortion_strength, // distort_xy
0.012 // frequency
);
vec4 color2 = ripple(p,
- 17.5 * in_distortion_strength, // distort_xy
+ 49 * in_distortion_strength, // distort_xy
0.018 // frequency
);
// Alpha blend between two layers.
@@ -128,8 +128,8 @@ class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) {
set(value) {
field = value * 0.001f
setUniform("in_time", field)
- setUniform("in_phase1", field * 2f + 0.367f)
- setUniform("in_phase2", field * 5.2f * 1.531f)
+ setUniform("in_phase1", field * 3f + 0.367f)
+ setUniform("in_phase2", field * 7.2f * 1.531f)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index 1d670628e365..fe5a69996eb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -81,7 +81,6 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
private final String mNetworkNameSeparator;
private final ContentObserver mObserver;
private final boolean mProviderModelBehavior;
- private final boolean mProviderModelSetting;
private final Handler mReceiverHandler;
private int mImsType = IMS_TYPE_WWAN;
// Save entire info for logging, we only use the id.
@@ -193,8 +192,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
SubscriptionDefaults defaults,
Looper receiverLooper,
CarrierConfigTracker carrierConfigTracker,
- FeatureFlags featureFlags,
- StatusBarFlags statusBarFlags
+ FeatureFlags featureFlags
) {
super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
@@ -229,7 +227,6 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
info, mDefaults, mMobileCallback);
mProviderModelBehavior = featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
- mProviderModelSetting = statusBarFlags.isProviderModelSettingEnabled();
}
void setConfiguration(Config config) {
@@ -396,10 +393,9 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
IconState qsIcon = null;
CharSequence qsDescription = null;
- boolean pm = mProviderModelSetting || mProviderModelBehavior;
if (mCurrentState.dataSim) {
// If using provider model behavior, only show QS icons if the state is also default
- if (pm && !mCurrentState.isDefault) {
+ if (!mCurrentState.isDefault) {
return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
}
@@ -814,7 +810,6 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
public void dump(PrintWriter pw) {
super.dump(pw);
pw.println(" mSubscription=" + mSubscriptionInfo + ",");
- pw.println(" mProviderModelSetting=" + mProviderModelSetting + ",");
pw.println(" mProviderModelBehavior=" + mProviderModelBehavior + ",");
pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
pw.println(" isDataDisabled=" + isDataDisabled() + ",");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 03d443e229a4..5272a16a4b72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -75,7 +75,6 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
-import com.android.systemui.qs.tiles.dialog.InternetDialogUtil;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -132,11 +131,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
private final DemoModeController mDemoModeController;
private final Object mLock = new Object();
private final boolean mProviderModelBehavior;
- private final boolean mProviderModelSetting;
private Config mConfig;
private final CarrierConfigTracker mCarrierConfigTracker;
private final FeatureFlags mFeatureFlags;
- private final StatusBarFlags mStatusBarFlags;
private final DumpManager mDumpManager;
private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
@@ -236,7 +233,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
@Main Handler handler,
InternetDialogFactory internetDialogFactory,
FeatureFlags featureFlags,
- StatusBarFlags statusBarFlags,
DumpManager dumpManager) {
this(context, connectivityManager,
telephonyManager,
@@ -256,7 +252,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
demoModeController,
carrierConfigTracker,
featureFlags,
- statusBarFlags,
dumpManager);
mReceiverHandler.post(mRegisterListeners);
mMainHandler = handler;
@@ -280,7 +275,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
FeatureFlags featureFlags,
- StatusBarFlags statusBarFlags,
DumpManager dumpManager
) {
mContext = context;
@@ -300,7 +294,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
mDemoModeController = demoModeController;
mCarrierConfigTracker = carrierConfigTracker;
mFeatureFlags = featureFlags;
- mStatusBarFlags = statusBarFlags;
mDumpManager = dumpManager;
// telephony
@@ -322,8 +315,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
}
});
mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
- mCallbackHandler, this, mWifiManager, mConnectivityManager, networkScoreManager,
- mStatusBarFlags);
+ mCallbackHandler, this, mWifiManager, mConnectivityManager, networkScoreManager);
mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this);
@@ -449,7 +441,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
mDemoModeController.addCallback(this);
mProviderModelBehavior = mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
- mProviderModelSetting = mStatusBarFlags.isProviderModelSettingEnabled();
mDumpManager.registerDumpable(TAG, this);
}
@@ -499,9 +490,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- if (InternetDialogUtil.isProviderModelEnabled(mContext)) {
- filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
- }
+ filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
@@ -734,9 +723,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
TelephonyIcons.FLIGHT_MODE_ICON,
mContext.getString(R.string.accessibility_airplane_mode)));
cb.setNoSims(mHasNoSubs, mSimDetected);
- if (mProviderModelSetting) {
- cb.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, mNoNetworksAvailable);
- }
+ cb.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, mNoNetworksAvailable);
mWifiSignalController.notifyListeners(cb);
mEthernetSignalController.notifyListeners(cb);
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -965,7 +952,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mHasMobileDataFeature, mPhone.createForSubscriptionId(subId),
mCallbackHandler, this, subscriptions.get(i),
mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
- mFeatureFlags, mStatusBarFlags);
+ mFeatureFlags);
controller.setUserSetupComplete(mUserSetup);
mMobileSignalControllers.put(subId, controller);
if (subscriptions.get(i).getSimSlotIndex() == 0) {
@@ -1125,8 +1112,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mobileSignalController.updateNoCallingState();
}
notifyAllListeners();
- } else if (mProviderModelSetting) {
- // TODO(b/191903788): Replace the flag name once the new flag is added.
+ } else {
mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
&& !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
&& !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
@@ -1443,7 +1429,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mConfig, mHasMobileDataFeature,
mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this,
info, mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
- mFeatureFlags, mStatusBarFlags);
+ mFeatureFlags);
mMobileSignalControllers.put(id, controller);
controller.getState().userSetup = true;
return info;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/StatusBarFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/StatusBarFlags.java
deleted file mode 100644
index 89d4bf5877cd..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/StatusBarFlags.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.connectivity;
-
-import android.content.Context;
-import android.util.FeatureFlagUtils;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
-
-import javax.inject.Inject;
-
-/**
- * Class for providing StatusBar specific logic around {@link FeatureFlags}.
- */
-@SysUISingleton
-public class StatusBarFlags {
- private final Context mContext;
-
- @Inject
- public StatusBarFlags(Context context) {
- mContext = context;
- }
-
- /** System setting for provider model behavior */
- public boolean isProviderModelSettingEnabled() {
- return FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 89fe24ff91c4..5361a6716044 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -44,7 +44,6 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup>
private final IconGroup mUnmergedWifiIconGroup = WifiIcons.UNMERGED_WIFI;
private final MobileIconGroup mCarrierMergedWifiIconGroup = TelephonyIcons.CARRIER_MERGED_WIFI;
private final WifiManager mWifiManager;
- private final boolean mProviderModelSetting;
public WifiSignalController(
Context context,
@@ -53,8 +52,7 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup>
NetworkControllerImpl networkController,
WifiManager wifiManager,
ConnectivityManager connectivityManager,
- NetworkScoreManager networkScoreManager,
- StatusBarFlags statusBarFlags) {
+ NetworkScoreManager networkScoreManager) {
super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
callbackHandler, networkController);
mWifiManager = wifiManager;
@@ -67,7 +65,6 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup>
new WifiTrafficStateCallback());
}
mCurrentState.iconGroup = mLastState.iconGroup = mUnmergedWifiIconGroup;
- mProviderModelSetting = statusBarFlags.isProviderModelSettingEnabled();
}
@Override
@@ -104,37 +101,22 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup>
if (mCurrentState.inetCondition == 0) {
contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet));
}
- if (mProviderModelSetting) {
- IconState statusIcon = new IconState(
- wifiVisible, getCurrentIconId(), contentDescription);
- IconState qsIcon = null;
- if (mCurrentState.isDefault || (!mNetworkController.isRadioOn()
- && !mNetworkController.isEthernetDefault())) {
- qsIcon = new IconState(mCurrentState.connected,
- mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
- : getQsCurrentIconId(), contentDescription);
- }
- WifiIndicators wifiIndicators = new WifiIndicators(
- mCurrentState.enabled, statusIcon, qsIcon,
- ssidPresent && mCurrentState.activityIn,
- ssidPresent && mCurrentState.activityOut,
- wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel
- );
- callback.setWifiIndicators(wifiIndicators);
- } else {
- IconState statusIcon = new IconState(
- wifiVisible, getCurrentIconId(), contentDescription);
- IconState qsIcon = new IconState(mCurrentState.connected,
+ IconState statusIcon = new IconState(
+ wifiVisible, getCurrentIconId(), contentDescription);
+ IconState qsIcon = null;
+ if (mCurrentState.isDefault || (!mNetworkController.isRadioOn()
+ && !mNetworkController.isEthernetDefault())) {
+ qsIcon = new IconState(mCurrentState.connected,
mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
: getQsCurrentIconId(), contentDescription);
- WifiIndicators wifiIndicators = new WifiIndicators(
- mCurrentState.enabled, statusIcon, qsIcon,
- ssidPresent && mCurrentState.activityIn,
- ssidPresent && mCurrentState.activityOut,
- wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel
- );
- callback.setWifiIndicators(wifiIndicators);
}
+ WifiIndicators wifiIndicators = new WifiIndicators(
+ mCurrentState.enabled, statusIcon, qsIcon,
+ ssidPresent && mCurrentState.activityIn,
+ ssidPresent && mCurrentState.activityOut,
+ wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel
+ );
+ callback.setWifiIndicators(wifiIndicators);
}
private void notifyListenersForCarrierWifi(SignalCallback callback) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index a44de2ce5699..a4e2d5ec0829 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -241,6 +241,10 @@ class LockscreenSmartspaceController @Inject constructor(
configurationController.addCallback(configChangeListener)
statusBarStateController.addCallback(statusBarStateListener)
+ plugin.registerSmartspaceEventNotifier {
+ e -> session?.notifySmartspaceEvent(e)
+ }
+
reloadSmartspace()
}
@@ -266,6 +270,7 @@ class LockscreenSmartspaceController @Inject constructor(
statusBarStateController.removeCallback(statusBarStateListener)
session = null
+ plugin?.registerSmartspaceEventNotifier(null)
plugin?.onTargetsAvailable(emptyList())
Log.d(TAG, "Ending smartspace session for lockscreen")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index bfe352d57c24..7269f5545163 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -146,6 +146,7 @@ public class NotificationSnooze extends LinearLayout
protected void onAttachedToWindow() {
super.onAttachedToWindow();
logOptionSelection(MetricsEvent.NOTIFICATION_SNOOZE_CLICKED, mDefaultOption);
+ dispatchConfigurationChanged(getResources().getConfiguration());
}
@Override
@@ -254,7 +255,7 @@ public class NotificationSnooze extends LinearLayout
return new NotificationSnoozeOption(null, minutes, description, resultText, action);
}
SpannableString string = new SpannableString(resultText);
- string.setSpan(new StyleSpan(Typeface.BOLD),
+ string.setSpan(new StyleSpan(Typeface.BOLD, res.getConfiguration().fontWeightAdjustment),
index, index + description.length(), 0 /* flags */);
return new NotificationSnoozeOption(null, minutes, description, string,
action);
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 518788b5a6dd..79b05c910705 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
@@ -412,7 +412,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private NotificationShelf mShelf;
private int mMaxDisplayedNotifications = -1;
private float mKeyguardBottomPadding = -1;
- private int mStatusBarHeight;
+ @VisibleForTesting int mStatusBarHeight;
private int mMinInteractionHeight;
private final Rect mClipRect = new Rect();
private boolean mIsClipped;
@@ -4854,8 +4854,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getMinExpansionHeight() {
+ // shelf height is defined in dp but status bar height can be defined in px, that makes
+ // relation between them variable - sometimes one might be bigger than the other when
+ // changing density. That’s why we need to ensure we’re not subtracting negative value below
return mShelf.getIntrinsicHeight()
- - (mShelf.getIntrinsicHeight() - mStatusBarHeight + mWaterfallTopInset) / 2
+ - Math.max(0,
+ (mShelf.getIntrinsicHeight() - mStatusBarHeight + mWaterfallTopInset) / 2)
+ mWaterfallTopInset;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index d87a02493a23..1b42b58a55aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.PowerManager;
@@ -26,7 +27,10 @@ import android.util.Log;
import android.util.MathUtils;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -36,13 +40,18 @@ import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashSet;
+import java.util.Optional;
import java.util.Set;
import javax.inject.Inject;
@@ -54,7 +63,8 @@ import javax.inject.Inject;
public class DozeParameters implements
TunerService.Tunable,
com.android.systemui.plugins.statusbar.DozeParameters,
- Dumpable {
+ Dumpable, ConfigurationController.ConfigurationListener,
+ StatusBarStateController.StateListener, FoldAodAnimationController.FoldAodAnimationStatus {
private static final int MAX_DURATION = 60 * 1000;
public static final boolean FORCE_NO_BLANKING =
SystemProperties.getBoolean("debug.force_no_blanking", false);
@@ -69,12 +79,30 @@ public class DozeParameters implements
private final BatteryController mBatteryController;
private final FeatureFlags mFeatureFlags;
private final ScreenOffAnimationController mScreenOffAnimationController;
+ private final FoldAodAnimationController mFoldAodAnimationController;
+ private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private final Set<Callback> mCallbacks = new HashSet<>();
private boolean mDozeAlwaysOn;
private boolean mControlScreenOffAnimation;
+ private boolean mKeyguardShowing;
+ @VisibleForTesting
+ final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ mKeyguardShowing = showing;
+ updateControlScreenOff();
+ }
+
+ @Override
+ public void onShadeExpandedChanged(boolean expanded) {
+ updateControlScreenOff();
+ }
+ };
+
@Inject
protected DozeParameters(
@Main Resources resources,
@@ -85,7 +113,12 @@ public class DozeParameters implements
TunerService tunerService,
DumpManager dumpManager,
FeatureFlags featureFlags,
- ScreenOffAnimationController screenOffAnimationController) {
+ ScreenOffAnimationController screenOffAnimationController,
+ Optional<SysUIUnfoldComponent> sysUiUnfoldComponent,
+ UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ ConfigurationController configurationController,
+ StatusBarStateController statusBarStateController) {
mResources = resources;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAlwaysOnPolicy = alwaysOnDisplayPolicy;
@@ -97,11 +130,22 @@ public class DozeParameters implements
mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
mFeatureFlags = featureFlags;
mScreenOffAnimationController = screenOffAnimationController;
+ mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+ keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
tunerService.addTunable(
this,
Settings.Secure.DOZE_ALWAYS_ON,
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+ configurationController.addCallback(this);
+ statusBarStateController.addCallback(this);
+
+ mFoldAodAnimationController = sysUiUnfoldComponent
+ .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
+
+ if (mFoldAodAnimationController != null) {
+ mFoldAodAnimationController.addCallback(this);
+ }
}
public boolean getDisplayStateSupported() {
@@ -222,13 +266,26 @@ public class DozeParameters implements
mPowerManager.setDozeAfterScreenOff(!controlScreenOffAnimation);
}
+ public void updateControlScreenOff() {
+ if (!getDisplayNeedsBlanking()) {
+ final boolean controlScreenOff =
+ getAlwaysOn() && (mKeyguardShowing || shouldControlUnlockedScreenOff());
+ setControlScreenOffAnimation(controlScreenOff);
+ }
+ }
+
/**
* Whether we want to control the screen off animation when the device is unlocked. If we do,
* we'll animate in AOD before turning off the screen, rather than simply fading to black and
* then abruptly showing AOD.
+ *
+ * There are currently several reasons we might not want to control the screen off even if we
+ * are able to, such as the shade being expanded, being in landscape, or having animations
+ * disabled for a11y.
*/
public boolean shouldControlUnlockedScreenOff() {
- return mScreenOffAnimationController.shouldControlUnlockedScreenOff();
+ return canControlUnlockedScreenOff()
+ && mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation();
}
public boolean shouldDelayKeyguardShow() {
@@ -325,6 +382,11 @@ public class DozeParameters implements
@Override
public void onTuningChanged(String key, String newValue) {
mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
+
+ if (key.equals(Settings.Secure.DOZE_ALWAYS_ON)) {
+ updateControlScreenOff();
+ }
+
for (Callback callback : mCallbacks) {
callback.onAlwaysOnChange();
}
@@ -332,6 +394,21 @@ public class DozeParameters implements
}
@Override
+ public void onConfigChanged(Configuration newConfig) {
+ updateControlScreenOff();
+ }
+
+ @Override
+ public void onStatePostChange() {
+ updateControlScreenOff();
+ }
+
+ @Override
+ public void onFoldToAodAnimationChanged() {
+ updateControlScreenOff();
+ }
+
+ @Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.print("getAlwaysOn(): "); pw.println(getAlwaysOn());
pw.print("getDisplayStateSupported(): "); pw.println(getDisplayStateSupported());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 57b9c03ce576..a88a3b6392c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -63,7 +63,6 @@ public final class DozeServiceHost implements DozeHost {
private final DozeLog mDozeLog;
private final PowerManager mPowerManager;
private boolean mAnimateWakeup;
- private boolean mAnimateScreenOff;
private boolean mIgnoreTouchWhilePulsing;
private Runnable mPendingScreenOffCallback;
@VisibleForTesting
@@ -357,11 +356,6 @@ public final class DozeServiceHost implements DozeHost {
}
@Override
- public void setAnimateScreenOff(boolean animateScreenOff) {
- mAnimateScreenOff = animateScreenOff;
- }
-
- @Override
public void onSlpiTap(float screenX, float screenY) {
if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
&& mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
@@ -440,10 +434,6 @@ public final class DozeServiceHost implements DozeHost {
return mAnimateWakeup;
}
- boolean shouldAnimateScreenOff() {
- return mAnimateScreenOff;
- }
-
boolean getIgnoreTouchWhilePulsing() {
return mIgnoreTouchWhilePulsing;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index e7d5724fa9bf..81871634fbaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -392,7 +392,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
}
float alphaQsExpansion = 1 - Math.min(
- 1, mNotificationPanelViewStateProvider.getQsExpansionFraction() * 2);
+ 1, mNotificationPanelViewStateProvider.getLockscreenShadeDragProgress() * 2);
float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
* mKeyguardStatusBarAnimateAlpha
* (1.0f - mKeyguardHeadsUpShowingAmount);
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 3b7063e6662f..434671c02035 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.view.View.GONE;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static androidx.constraintlayout.widget.ConstraintSet.END;
import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
@@ -33,7 +34,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_CLOSED;
@@ -466,6 +466,9 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mIsFullWidth;
private boolean mBlockingExpansionForCurrentTouch;
+ // TODO (b/204204226): no longer needed once refactor is complete
+ private final boolean mUseCombinedQSHeaders;
+
/**
* Following variables maintain state of events when input focus transfer may occur.
*/
@@ -913,6 +916,8 @@ public class NotificationPanelViewController extends PanelViewController {
mQsFrameTranslateController = qsFrameTranslateController;
updateUserSwitcherFlags();
onFinishInflate();
+
+ mUseCombinedQSHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS);
}
private void onFinishInflate() {
@@ -1142,6 +1147,9 @@ public class NotificationPanelViewController extends PanelViewController {
} else {
constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END);
constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START);
+ if (mUseCombinedQSHeaders) {
+ constraintSet.constrainHeight(R.id.split_shade_status_bar, WRAP_CONTENT);
+ }
}
constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth;
constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth;
@@ -1392,7 +1400,6 @@ public class NotificationPanelViewController extends PanelViewController {
stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
}
- mSplitShadeHeaderController.setShadeExpandedFraction(getExpandedFraction());
mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding);
mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
@@ -2401,7 +2408,6 @@ public class NotificationPanelViewController extends PanelViewController {
? 1f : computeQsExpansionFraction();
mQs.setQsExpansion(adjustedExpansionFraction, getExpandedFraction(), getHeaderTranslation(),
squishiness);
- mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
@@ -2415,6 +2421,17 @@ public class NotificationPanelViewController extends PanelViewController {
mDepthController.setQsPanelExpansion(qsExpansionFraction);
+ // updateQsExpansion will get called whenever mTransitionToFullShadeProgress or
+ // mLockscreenShadeTransitionController.getDragProgress change.
+ // When in lockscreen, getDragProgress indicates the true expanded fraction of QS
+ float shadeExpandedFraction = mTransitioningToFullShadeProgress > 0
+ ? mLockscreenShadeTransitionController.getDragProgress()
+ : getExpandedFraction();
+ mSplitShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
+ mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
+ mSplitShadeHeaderController.setShadeExpanded(mQsVisible);
+
+
if (mCommunalViewController != null) {
mCommunalViewController.updateQsExpansion(qsExpansionFraction);
}
@@ -3629,11 +3646,15 @@ public class NotificationPanelViewController extends PanelViewController {
return !isFullWidth() || !mShowIconsWhenExpanded;
}
- public final QS.ScrollListener mScrollListener = scrollY -> {
- if (scrollY > 0 && !mQsFullyExpanded) {
- if (DEBUG) Log.d(TAG, "Scrolling while not expanded. Forcing expand");
- // If we are scrolling QS, we should be fully expanded.
- expandWithQs();
+ public final QS.ScrollListener mScrollListener = new QS.ScrollListener() {
+ @Override
+ public void onQsPanelScrollChanged(int scrollY) {
+ mSplitShadeHeaderController.setQsScrollY(scrollY);
+ if (scrollY > 0 && !mQsFullyExpanded) {
+ if (DEBUG) Log.d(TAG, "Scrolling while not expanded. Forcing expand");
+ // If we are scrolling QS, we should be fully expanded.
+ expandWithQs();
+ }
}
};
@@ -4684,8 +4705,6 @@ public class NotificationPanelViewController extends PanelViewController {
// would reset
maybeAnimateBottomAreaAlpha();
updateQsState();
- mSplitShadeHeaderController.setShadeExpanded(
- mBarState == SHADE || mBarState == SHADE_LOCKED);
}
@Override
@@ -4715,6 +4734,9 @@ public class NotificationPanelViewController extends PanelViewController {
* {@link KeyguardStatusBarViewController} and remove this method.
*/
boolean shouldHeadsUpBeVisible();
+
+ /** Return the fraction of the shade that's expanded, when in lockscreen. */
+ float getLockscreenShadeDragProgress();
}
private final NotificationPanelViewStateProvider mNotificationPanelViewStateProvider =
@@ -4733,6 +4755,11 @@ public class NotificationPanelViewController extends PanelViewController {
public boolean shouldHeadsUpBeVisible() {
return mHeadsUpAppearanceController.shouldBeVisible();
}
+
+ @Override
+ public float getLockscreenShadeDragProgress() {
+ return mLockscreenShadeTransitionController.getDragProgress();
+ }
};
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index 8cf7288c9cd5..a1be5acdac13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -19,17 +19,22 @@ package com.android.systemui.statusbar.phone
import android.view.View
import androidx.constraintlayout.motion.widget.MotionLayout
import com.android.settingslib.Utils
+import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.qs.ChipVisibilityListener
import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_BATTERY_CONTROLLER
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_HEADER
+import java.io.FileDescriptor
+import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Named
@@ -40,12 +45,23 @@ class SplitShadeHeaderController @Inject constructor(
private val privacyIconsController: HeaderPrivacyIconsController,
qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
featureFlags: FeatureFlags,
- @Named(SPLIT_SHADE_BATTERY_CONTROLLER) batteryMeterViewController: BatteryMeterViewController
-) {
+ @Named(SPLIT_SHADE_BATTERY_CONTROLLER) batteryMeterViewController: BatteryMeterViewController,
+ dumpManager: DumpManager
+) : Dumpable {
companion object {
private val HEADER_TRANSITION_ID = R.id.header_transition
private val SPLIT_HEADER_TRANSITION_ID = R.id.split_header_transition
+ private val QQS_HEADER_CONSTRAINT = R.id.qqs_header_constraint
+ private val QS_HEADER_CONSTRAINT = R.id.qs_header_constraint
+ private val SPLIT_HEADER_CONSTRAINT = R.id.split_header_constraint
+
+ private fun Int.stateToString() = when (this) {
+ QQS_HEADER_CONSTRAINT -> "QQS Header"
+ QS_HEADER_CONSTRAINT -> "QS Header"
+ SPLIT_HEADER_CONSTRAINT -> "Split Header"
+ else -> "Unknown state"
+ }
}
private val combinedHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)
@@ -97,16 +113,37 @@ class SplitShadeHeaderController @Inject constructor(
}
}
+ var qsScrollY = 0
+ set(value) {
+ if (field != value) {
+ field = value
+ updateScrollY()
+ }
+ }
+
+ private val chipVisibilityListener: ChipVisibilityListener = object : ChipVisibilityListener {
+ override fun onChipVisibilityRefreshed(visible: Boolean) {
+ if (statusBar is MotionLayout) {
+ val state = statusBar.getConstraintSet(QQS_HEADER_CONSTRAINT).apply {
+ setAlpha(R.id.statusIcons, if (visible) 0f else 1f)
+ setAlpha(R.id.batteryRemainingIcon, if (visible) 0f else 1f)
+ }
+ statusBar.updateState(QQS_HEADER_CONSTRAINT, state)
+ }
+ }
+ }
+
init {
if (statusBar is MotionLayout) {
val context = statusBar.context
val resources = statusBar.resources
- statusBar.getConstraintSet(R.id.qqs_header_constraint)
+ statusBar.getConstraintSet(QQS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qqs_header))
- statusBar.getConstraintSet(R.id.qs_header_constraint)
+ statusBar.getConstraintSet(QS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qs_header))
- statusBar.getConstraintSet(R.id.split_header_constraint)
+ statusBar.getConstraintSet(SPLIT_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.split_header))
+ privacyIconsController.chipVisibilityListener = chipVisibilityListener
}
}
@@ -134,10 +171,19 @@ class SplitShadeHeaderController @Inject constructor(
qsCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
.build()
+
+ dumpManager.registerDumpable(this)
+
updateVisibility()
updateConstraints()
}
+ private fun updateScrollY() {
+ if (!splitShadeMode && combinedHeaders) {
+ statusBar.scrollY = qsScrollY
+ }
+ }
+
private fun onShadeExpandedChanged() {
if (shadeExpanded) {
privacyIconsController.startListening()
@@ -149,7 +195,7 @@ class SplitShadeHeaderController @Inject constructor(
}
private fun onSplitShadeModeChanged() {
- if (splitShadeMode) {
+ if (splitShadeMode || combinedHeaders) {
privacyIconsController.onParentVisible()
} else {
privacyIconsController.onParentInvisible()
@@ -183,6 +229,7 @@ class SplitShadeHeaderController @Inject constructor(
statusBar.setTransition(HEADER_TRANSITION_ID)
statusBar.transitionToStart()
updatePosition()
+ updateScrollY()
}
}
@@ -211,4 +258,17 @@ class SplitShadeHeaderController @Inject constructor(
iconContainer.addIgnoredSlots(carrierIconSlots)
}
}
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.println("visible: $visible")
+ pw.println("shadeExpanded: $shadeExpanded")
+ pw.println("shadeExpandedFraction: $shadeExpandedFraction")
+ pw.println("splitShadeMode: $splitShadeMode")
+ pw.println("qsExpandedFraction: $qsExpandedFraction")
+ pw.println("qsScrollY: $qsScrollY")
+ if (combinedHeaders) {
+ statusBar as MotionLayout
+ pw.println("currentState: ${statusBar.currentState.stateToString()}")
+ }
+ }
}
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 07914cf1d9c4..2ba70df8a1da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3162,7 +3162,7 @@ public class StatusBar extends CoreStartable implements
boolean wakeAndUnlock = mBiometricUnlockController.getMode()
== BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup() && !wakeAndUnlock)
- || (mDozing && mDozeServiceHost.shouldAnimateScreenOff()
+ || (mDozing && mDozeParameters.shouldControlScreenOff()
&& visibleNotOccludedOrWillBe);
mNotificationPanelViewController.setDozing(mDozing, animate, mWakeUpTouchLocation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index fc661b920837..92c76dc196b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -229,10 +229,6 @@ class UnlockedScreenOffAnimationController @Inject constructor(
return false
}
- if (!dozeParameters.get().canControlUnlockedScreenOff()) {
- return false
- }
-
// If animations are disabled system-wide, don't play this one either.
if (Settings.Global.getString(
context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE) == "0") {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index e6fc49fae315..0b89ef28d227 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.graphics.PixelFormat
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
+import android.hardware.input.InputManager
import android.hardware.display.DisplayManager
import android.os.Handler
import android.os.Trace
@@ -195,8 +196,7 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor(
params.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
params.fitInsetsTypes = 0
- params.flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
+ params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
params.setTrustedOverlay()
val packageName: String = context.opPackageName
@@ -239,6 +239,8 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor(
if (scrimView == null) {
addView()
}
+ // Disable input dispatching during transition.
+ InputManager.getInstance().cancelCurrentTouch()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 8dd5d6c01394..08c77146d34c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -53,12 +54,14 @@ import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintStateListener;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.WindowManager;
@@ -67,6 +70,8 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.concurrency.Execution;
+import com.android.systemui.util.concurrency.FakeExecution;
import org.junit.Before;
import org.junit.Test;
@@ -112,20 +117,27 @@ public class AuthControllerTest extends SysuiTestCase {
private SidefpsController mSidefpsController;
@Mock
private DisplayManager mDisplayManager;
- @Mock
- private Handler mHandler;
@Captor
ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor;
+ @Captor
+ ArgumentCaptor<FingerprintStateListener> mFingerprintStateCaptor;
+ private TestableContext mContextSpy;
+ private Execution mExecution;
+ private TestableLooper mTestableLooper;
+ private Handler mHandler;
private TestableAuthController mAuthController;
@Before
public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this);
- TestableContext context = spy(mContext);
+ mContextSpy = spy(mContext);
+ mExecution = new FakeExecution();
+ mTestableLooper = TestableLooper.get(this);
+ mHandler = new Handler(mTestableLooper.getLooper());
- when(context.getPackageManager()).thenReturn(mPackageManager);
+ when(mContextSpy.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
.thenReturn(true);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
@@ -158,21 +170,78 @@ public class AuthControllerTest extends SysuiTestCase {
props.add(prop);
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
- mAuthController = new TestableAuthController(context, mCommandQueue,
+ mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
() -> mUdfpsController, () -> mSidefpsController);
mAuthController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
mAuthenticatorsRegisteredCaptor.capture());
+
mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props);
+
+ // Ensures that the operations posted on the handler get executed.
+ mTestableLooper.processAllMessages();
}
// Callback tests
@Test
+ public void testRegistersFingerprintStateListener_afterAllAuthenticatorsAreRegistered()
+ throws RemoteException {
+ // This test is sensitive to prior FingerprintManager interactions.
+ reset(mFingerprintManager);
+
+ // This test requires an uninitialized AuthController.
+ AuthController authController = new TestableAuthController(mContextSpy, mExecution,
+ mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
+ mFaceManager, () -> mUdfpsController, () -> mSidefpsController);
+ authController.start();
+
+ verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
+ mAuthenticatorsRegisteredCaptor.capture());
+ mTestableLooper.processAllMessages();
+
+ verify(mFingerprintManager, never()).registerFingerprintStateListener(any());
+
+ mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>());
+ mTestableLooper.processAllMessages();
+
+ verify(mFingerprintManager).registerFingerprintStateListener(any());
+ }
+
+ @Test
+ public void testDoesNotCrash_afterEnrollmentsChangedForUnknownSensor() throws RemoteException {
+ // This test is sensitive to prior FingerprintManager interactions.
+ reset(mFingerprintManager);
+
+ // This test requires an uninitialized AuthController.
+ AuthController authController = new TestableAuthController(mContextSpy, mExecution,
+ mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
+ mFaceManager, () -> mUdfpsController, () -> mSidefpsController);
+ authController.start();
+
+ verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
+ mAuthenticatorsRegisteredCaptor.capture());
+
+ // Emulates a device with no authenticators (empty list).
+ mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>());
+ mTestableLooper.processAllMessages();
+
+ verify(mFingerprintManager).registerFingerprintStateListener(
+ mFingerprintStateCaptor.capture());
+
+ // Enrollments changed for an unknown sensor.
+ mFingerprintStateCaptor.getValue().onEnrollmentsChanged(0 /* userId */,
+ 0xbeef /* sensorId */, true /* hasEnrollments */);
+ mTestableLooper.processAllMessages();
+
+ // Nothing should crash.
+ }
+
+ @Test
public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
- showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
+ showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
null /* credentialAttestation */);
verify(mReceiver).onDialogDismissed(
@@ -497,7 +566,7 @@ public class AuthControllerTest extends SysuiTestCase {
when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
mAuthController.mTaskStackListener.onTaskStackChanged();
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver);
@@ -528,7 +597,7 @@ public class AuthControllerTest extends SysuiTestCase {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mAuthController.mBroadcastReceiver.onReceive(mContext, intent);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver);
@@ -598,6 +667,7 @@ public class AuthControllerTest extends SysuiTestCase {
private PromptInfo mLastBiometricPromptInfo;
TestableAuthController(Context context,
+ Execution execution,
CommandQueue commandQueue,
ActivityTaskManager activityTaskManager,
WindowManager windowManager,
@@ -605,7 +675,7 @@ public class AuthControllerTest extends SysuiTestCase {
FaceManager faceManager,
Provider<UdfpsController> udfpsControllerFactory,
Provider<SidefpsController> sidefpsControllerFactory) {
- super(context, commandQueue, activityTaskManager, windowManager,
+ super(context, execution, commandQueue, activityTaskManager, windowManager,
fingerprintManager, faceManager, udfpsControllerFactory,
sidefpsControllerFactory, mDisplayManager, mHandler);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 2c4808a4b84f..5128ccc15d9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -131,7 +131,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */)
// THEN update sensor location and show ripple
- verify(rippleView).setSensorLocation(fpsLocation)
+ verify(rippleView).setFingerprintSensorLocation(fpsLocation, -1f)
verify(rippleView).startUnlockedRipple(any())
}
@@ -292,10 +292,10 @@ class AuthRippleControllerTest : SysuiTestCase() {
reset(rippleView)
captor.value.onThemeChanged()
- verify(rippleView).setColor(ArgumentMatchers.anyInt())
+ verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
reset(rippleView)
captor.value.onUiModeChanged()
- verify(rippleView).setColor(ArgumentMatchers.anyInt())
+ verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index 55af51d3fddf..e5a75e231f8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -27,8 +27,6 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,10 +41,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.unfold.FoldAodAnimationController;
-import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.After;
@@ -56,8 +51,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Optional;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DozeUiTest extends SysuiTestCase {
@@ -82,12 +75,6 @@ public class DozeUiTest extends SysuiTestCase {
private DozeUi mDozeUi;
@Mock
private StatusBarStateController mStatusBarStateController;
- @Mock
- private FoldAodAnimationController mFoldAodAnimationController;
- @Mock
- private SysUIUnfoldComponent mSysUIUnfoldComponent;
- @Mock
- private ConfigurationController mConfigurationController;
@Before
public void setUp() throws Exception {
@@ -98,13 +85,8 @@ public class DozeUiTest extends SysuiTestCase {
mWakeLock = new WakeLockFake();
mHandler = mHandlerThread.getThreadHandler();
- when(mSysUIUnfoldComponent.getFoldAodAnimationController())
- .thenReturn(mFoldAodAnimationController);
-
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
- mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- mStatusBarStateController, Optional.of(mSysUIUnfoldComponent),
- mConfigurationController);
+ mDozeParameters, mKeyguardUpdateMonitor, mStatusBarStateController, mDozeLog);
mDozeUi.setDozeMachine(mMachine);
}
@@ -116,7 +98,7 @@ public class DozeUiTest extends SysuiTestCase {
}
@Test
- public void pausingAndUnpausingAod_registersTimeTickAfterUnpausing() throws Exception {
+ public void pausingAndUnpausingAod_registersTimeTickAfterUnpausing() {
mDozeUi.transitionTo(UNINITIALIZED, INITIALIZED);
mDozeUi.transitionTo(INITIALIZED, DOZE_AOD);
mDozeUi.transitionTo(DOZE_AOD, DOZE_AOD_PAUSED);
@@ -129,60 +111,9 @@ public class DozeUiTest extends SysuiTestCase {
}
@Test
- public void propagatesAnimateScreenOff_noAlwaysOn() {
- reset(mHost);
- when(mDozeParameters.getAlwaysOn()).thenReturn(false);
- when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
- when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(true);
-
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
- verify(mHost).setAnimateScreenOff(eq(false));
- }
-
- @Test
- public void propagatesAnimateScreenOff_alwaysOn() {
- reset(mHost);
- when(mDozeParameters.getAlwaysOn()).thenReturn(true);
- when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
- when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(true);
-
- // Take over when the keyguard is visible.
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
- verify(mHost).setAnimateScreenOff(eq(true));
-
- // Do not animate screen-off when keyguard isn't visible - PowerManager will do it.
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
- verify(mHost).setAnimateScreenOff(eq(false));
- }
-
- @Test
- public void propagatesAnimateScreenOff_alwaysOn_shouldAnimateDozingChangeIsFalse() {
- reset(mHost);
- when(mDozeParameters.getAlwaysOn()).thenReturn(true);
- when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
- when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(false);
-
- // Take over when the keyguard is visible.
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
- verify(mHost).setAnimateScreenOff(eq(false));
- }
-
- @Test
- public void neverAnimateScreenOff_whenNotSupported() {
- // Re-initialize DozeParameters saying that the display requires blanking.
- reset(mDozeParameters);
- reset(mHost);
- when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
- mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
- mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- mStatusBarStateController, Optional.of(mSysUIUnfoldComponent),
- mConfigurationController);
- mDozeUi.setDozeMachine(mMachine);
-
- // Never animate if display doesn't support it.
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
- verify(mHost, never()).setAnimateScreenOff(eq(false));
+ public void transitionSetsAnimateWakeup_noAlwaysOn() {
+ mDozeUi.transitionTo(UNINITIALIZED, DOZE);
+ verify(mHost).setAnimateWakeup(eq(false));
}
@Test
@@ -192,49 +123,4 @@ public class DozeUiTest extends SysuiTestCase {
mDozeUi.transitionTo(UNINITIALIZED, DOZE);
verify(mHost).setAnimateWakeup(eq(true));
}
-
- @Test
- public void keyguardVisibility_changesControlScreenOffAnimation() {
- // Pre-condition
- reset(mDozeParameters);
- when(mDozeParameters.getAlwaysOn()).thenReturn(true);
- when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
-
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
- verify(mDozeParameters).setControlScreenOffAnimation(eq(false));
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
- verify(mDozeParameters).setControlScreenOffAnimation(eq(true));
- }
-
- @Test
- public void transitionSetsAnimateWakeup_noAlwaysOn() {
- mDozeUi.transitionTo(UNINITIALIZED, DOZE);
- verify(mHost).setAnimateWakeup(eq(false));
- }
-
- @Test
- public void controlScreenOffTrueWhenKeyguardNotShowingAndControlUnlockedScreenOff() {
- when(mDozeParameters.getAlwaysOn()).thenReturn(true);
- when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
-
- // Tell doze that keyguard is not visible.
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false /* showing */);
-
- // Since we're controlling the unlocked screen off animation, verify that we've asked to
- // control the screen off animation despite being unlocked.
- verify(mDozeParameters).setControlScreenOffAnimation(true);
- }
-
- @Test
- public void controlScreenOffFalseWhenKeyguardNotShowingAndControlUnlockedScreenOffFalse() {
- when(mDozeParameters.getAlwaysOn()).thenReturn(true);
- when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(false);
-
- // Tell doze that keyguard is not visible.
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false /* showing */);
-
- // Since we're not controlling the unlocked screen off animation, verify that we haven't
- // asked to control the screen off animation since we're unlocked.
- verify(mDozeParameters).setControlScreenOffAnimation(false);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/AppWidgetOverlayProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java
index 0fc306b99b65..736556871376 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/AppWidgetOverlayProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java
@@ -33,8 +33,8 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
-import com.android.systemui.dreams.appwidgets.AppWidgetOverlayProvider;
import com.android.systemui.dreams.appwidgets.AppWidgetProvider;
+import com.android.systemui.dreams.appwidgets.ComplicationProvider;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -48,7 +48,7 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-public class AppWidgetOverlayProviderTest extends SysuiTestCase {
+public class ComplicationProviderTest extends SysuiTestCase {
@Mock
ActivityStarter mActivityStarter;
@@ -62,10 +62,10 @@ public class AppWidgetOverlayProviderTest extends SysuiTestCase {
AppWidgetHostView mAppWidgetHostView;
@Mock
- OverlayHost.CreationCallback mCreationCallback;
+ ComplicationHost.CreationCallback mCreationCallback;
@Mock
- OverlayHost.InteractionCallback mInteractionCallback;
+ ComplicationHost.InteractionCallback mInteractionCallback;
@Mock
PendingIntent mPendingIntent;
@@ -73,7 +73,7 @@ public class AppWidgetOverlayProviderTest extends SysuiTestCase {
@Mock
RemoteViews.RemoteResponse mRemoteResponse;
- AppWidgetOverlayProvider mOverlayProvider;
+ ComplicationProvider mComplicationProvider;
RemoteViews.InteractionHandler mInteractionHandler;
@@ -84,8 +84,9 @@ public class AppWidgetOverlayProviderTest extends SysuiTestCase {
public SysuiTestableContext mContext = new SysuiTestableContext(
InstrumentationRegistry.getContext(), mLeakCheck);
- OverlayHostView.LayoutParams mLayoutParams = new OverlayHostView.LayoutParams(
- OverlayHostView.LayoutParams.MATCH_PARENT, OverlayHostView.LayoutParams.MATCH_PARENT);
+ ComplicationHostView.LayoutParams mLayoutParams = new ComplicationHostView.LayoutParams(
+ ComplicationHostView.LayoutParams.MATCH_PARENT,
+ ComplicationHostView.LayoutParams.MATCH_PARENT);
@Before
public void setup() {
@@ -93,7 +94,7 @@ public class AppWidgetOverlayProviderTest extends SysuiTestCase {
when(mPendingIntent.isActivity()).thenReturn(true);
when(mAppWidgetProvider.getWidget(mComponentName)).thenReturn(mAppWidgetHostView);
- mOverlayProvider = new AppWidgetOverlayProvider(
+ mComplicationProvider = new ComplicationProvider(
mActivityStarter,
mComponentName,
mAppWidgetProvider,
@@ -103,7 +104,8 @@ public class AppWidgetOverlayProviderTest extends SysuiTestCase {
final ArgumentCaptor<RemoteViews.InteractionHandler> creationCallbackCapture =
ArgumentCaptor.forClass(RemoteViews.InteractionHandler.class);
- mOverlayProvider.onCreateOverlay(mContext, mCreationCallback, mInteractionCallback);
+ mComplicationProvider.onCreateComplication(mContext, mCreationCallback,
+ mInteractionCallback);
verify(mAppWidgetHostView, times(1))
.setInteractionHandler(creationCallbackCapture.capture());
mInteractionHandler = creationCallbackCapture.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 904ee91c2642..5fa710f284b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -74,7 +74,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
WindowManagerImpl mWindowManager;
@Mock
- OverlayProvider mProvider;
+ ComplicationProvider mProvider;
@Mock
DreamOverlayStateController mDreamOverlayStateController;
@@ -124,16 +124,16 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
verify(mWindowManager).addView(any(), any());
// Add overlay.
- service.addOverlay(mProvider);
+ service.addComplication(mProvider);
mMainExecutor.runAllReady();
- final ArgumentCaptor<OverlayHost.CreationCallback> creationCallbackCapture =
- ArgumentCaptor.forClass(OverlayHost.CreationCallback.class);
- final ArgumentCaptor<OverlayHost.InteractionCallback> interactionCallbackCapture =
- ArgumentCaptor.forClass(OverlayHost.InteractionCallback.class);
+ final ArgumentCaptor<ComplicationHost.CreationCallback> creationCallbackCapture =
+ ArgumentCaptor.forClass(ComplicationHost.CreationCallback.class);
+ final ArgumentCaptor<ComplicationHost.InteractionCallback> interactionCallbackCapture =
+ ArgumentCaptor.forClass(ComplicationHost.InteractionCallback.class);
// Ensure overlay provider is asked to create view.
- verify(mProvider).onCreateOverlay(any(), creationCallbackCapture.capture(),
+ verify(mProvider).onCreateComplication(any(), creationCallbackCapture.capture(),
interactionCallbackCapture.capture());
mMainExecutor.runAllReady();
@@ -169,12 +169,12 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
verify(mDreamOverlayStateController).addCallback(callbackCapture.capture());
- when(mDreamOverlayStateController.getOverlays()).thenReturn(Arrays.asList(mProvider));
- callbackCapture.getValue().onOverlayChanged();
+ when(mDreamOverlayStateController.getComplications()).thenReturn(Arrays.asList(mProvider));
+ callbackCapture.getValue().onComplicationsChanged();
mMainExecutor.runAllReady();
// Verify provider is asked to create overlay.
- verify(mProvider).onCreateOverlay(any(), any(), any());
+ verify(mProvider).onCreateComplication(any(), any(), any());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 4e97be37603e..efc3c7c7611a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -27,6 +27,10 @@ import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import com.google.common.util.concurrent.ListenableFuture;
import org.junit.Before;
import org.junit.Test;
@@ -43,7 +47,9 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
DreamOverlayStateController.Callback mCallback;
@Mock
- OverlayProvider mProvider;
+ ComplicationProvider mProvider;
+
+ final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setup() {
@@ -51,36 +57,43 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
}
@Test
- public void testCallback() {
- final DreamOverlayStateController stateController = new DreamOverlayStateController();
+ public void testCallback() throws Exception {
+ final DreamOverlayStateController stateController = new DreamOverlayStateController(
+ mExecutor);
stateController.addCallback(mCallback);
- // Add overlay and verify callback is notified.
- final DreamOverlayStateController.OverlayToken token =
- stateController.addOverlay(mProvider);
+ // Add complication and verify callback is notified.
+ final ListenableFuture<DreamOverlayStateController.ComplicationToken> tokenFuture =
+ stateController.addComplication(mProvider);
- verify(mCallback, times(1)).onOverlayChanged();
+ mExecutor.runAllReady();
- final Collection<OverlayProvider> providers = stateController.getOverlays();
+ verify(mCallback, times(1)).onComplicationsChanged();
+
+ final Collection<ComplicationProvider> providers = stateController.getComplications();
assertEquals(providers.size(), 1);
assertTrue(providers.contains(mProvider));
clearInvocations(mCallback);
- // Remove overlay and verify callback is notified.
- stateController.removeOverlay(token);
- verify(mCallback, times(1)).onOverlayChanged();
+ // Remove complication and verify callback is notified.
+ stateController.removeComplication(tokenFuture.get());
+ mExecutor.runAllReady();
+ verify(mCallback, times(1)).onComplicationsChanged();
assertTrue(providers.isEmpty());
}
@Test
public void testNotifyOnCallbackAdd() {
- final DreamOverlayStateController stateController = new DreamOverlayStateController();
- final DreamOverlayStateController.OverlayToken token =
- stateController.addOverlay(mProvider);
+ final DreamOverlayStateController stateController =
+ new DreamOverlayStateController(mExecutor);
+
+ stateController.addComplication(mProvider);
+ mExecutor.runAllReady();
// Verify callback occurs on add when an overlay is already present.
stateController.addCallback(mCallback);
- verify(mCallback, times(1)).onOverlayChanged();
+ mExecutor.runAllReady();
+ verify(mCallback, times(1)).onComplicationsChanged();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationPrimerTest.java
index 2e5b1653584b..adf110bb2494 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationPrimerTest.java
@@ -37,7 +37,7 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.dreams.dagger.AppWidgetOverlayComponent;
+import com.android.systemui.dreams.appwidgets.dagger.AppWidgetComponent;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
@@ -50,7 +50,7 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-public class AppWidgetOverlayPrimerTest extends SysuiTestCase {
+public class ComplicationPrimerTest extends SysuiTestCase {
@Rule
public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
@@ -62,56 +62,57 @@ public class AppWidgetOverlayPrimerTest extends SysuiTestCase {
Resources mResources;
@Mock
- AppWidgetOverlayComponent mAppWidgetOverlayComponent1;
+ AppWidgetComponent mAppWidgetComplicationComponent1;
@Mock
- AppWidgetOverlayComponent mAppWidgetOverlayComponent2;
+ AppWidgetComponent mAppWidgetComplicationComponent2;
@Mock
- AppWidgetOverlayProvider mAppWidgetOverlayProvider1;
+ ComplicationProvider mComplicationProvider1;
@Mock
- AppWidgetOverlayProvider mAppWidgetOverlayProvider2;
+ ComplicationProvider mComplicationProvider2;
- final ComponentName mAppOverlayComponent1 =
+ final ComponentName mAppComplicationComponent1 =
ComponentName.unflattenFromString("com.foo.bar/.Baz");
- final ComponentName mAppOverlayComponent2 =
+ final ComponentName mAppComplicationComponent2 =
ComponentName.unflattenFromString("com.foo.bar/.Baz2");
- final int mAppOverlayGravity1 = Gravity.BOTTOM | Gravity.START;
- final int mAppOverlayGravity2 = Gravity.BOTTOM | Gravity.END;
+ final int mAppComplicationGravity1 = Gravity.BOTTOM | Gravity.START;
+ final int mAppComplicationGravity2 = Gravity.BOTTOM | Gravity.END;
- final String[] mComponents = new String[]{mAppOverlayComponent1.flattenToString(),
- mAppOverlayComponent2.flattenToString() };
- final int[] mPositions = new int[]{ mAppOverlayGravity1, mAppOverlayGravity2 };
+ final String[] mComponents = new String[]{mAppComplicationComponent1.flattenToString(),
+ mAppComplicationComponent2.flattenToString() };
+ final int[] mPositions = new int[]{mAppComplicationGravity1, mAppComplicationGravity2};
@Mock
DreamOverlayStateController mDreamOverlayStateController;
@Mock
- AppWidgetOverlayComponent.Factory mAppWidgetOverlayProviderFactory;
+ AppWidgetComponent.Factory mAppWidgetComplicationProviderFactory;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- when(mAppWidgetOverlayProviderFactory.build(eq(mAppOverlayComponent1), any()))
- .thenReturn(mAppWidgetOverlayComponent1);
- when(mAppWidgetOverlayComponent1.getAppWidgetOverlayProvider())
- .thenReturn(mAppWidgetOverlayProvider1);
- when(mAppWidgetOverlayProviderFactory.build(eq(mAppOverlayComponent2), any()))
- .thenReturn(mAppWidgetOverlayComponent2);
- when(mAppWidgetOverlayComponent2.getAppWidgetOverlayProvider())
- .thenReturn(mAppWidgetOverlayProvider2);
- when(mResources.getIntArray(R.array.config_dreamOverlayPositions)).thenReturn(mPositions);
- when(mResources.getStringArray(R.array.config_dreamOverlayComponents))
+ when(mAppWidgetComplicationProviderFactory.build(eq(mAppComplicationComponent1), any()))
+ .thenReturn(mAppWidgetComplicationComponent1);
+ when(mAppWidgetComplicationComponent1.getAppWidgetComplicationProvider())
+ .thenReturn(mComplicationProvider1);
+ when(mAppWidgetComplicationProviderFactory.build(eq(mAppComplicationComponent2), any()))
+ .thenReturn(mAppWidgetComplicationComponent2);
+ when(mAppWidgetComplicationComponent2.getAppWidgetComplicationProvider())
+ .thenReturn(mComplicationProvider2);
+ when(mResources.getIntArray(R.array.config_dreamComplicationPositions))
+ .thenReturn(mPositions);
+ when(mResources.getStringArray(R.array.config_dreamAppWidgetComplications))
.thenReturn(mComponents);
}
@Test
public void testLoading() {
- final AppWidgetOverlayPrimer primer = new AppWidgetOverlayPrimer(mContext,
+ final ComplicationPrimer primer = new ComplicationPrimer(mContext,
mResources,
mDreamOverlayStateController,
- mAppWidgetOverlayProviderFactory);
+ mAppWidgetComplicationProviderFactory);
// Inform primer to begin.
primer.onBootCompleted();
@@ -120,7 +121,8 @@ public class AppWidgetOverlayPrimerTest extends SysuiTestCase {
{
final ArgumentCaptor<ConstraintLayout.LayoutParams> layoutParamsArgumentCaptor =
ArgumentCaptor.forClass(ConstraintLayout.LayoutParams.class);
- verify(mAppWidgetOverlayProviderFactory, times(1)).build(eq(mAppOverlayComponent1),
+ verify(mAppWidgetComplicationProviderFactory, times(1))
+ .build(eq(mAppComplicationComponent1),
layoutParamsArgumentCaptor.capture());
assertEquals(layoutParamsArgumentCaptor.getValue().startToStart,
@@ -129,14 +131,15 @@ public class AppWidgetOverlayPrimerTest extends SysuiTestCase {
ConstraintLayout.LayoutParams.PARENT_ID);
verify(mDreamOverlayStateController, times(1))
- .addOverlay(eq(mAppWidgetOverlayProvider1));
+ .addComplication(eq(mComplicationProvider1));
}
// Verify the second component is added to the state controller with the proper position.
{
final ArgumentCaptor<ConstraintLayout.LayoutParams> layoutParamsArgumentCaptor =
ArgumentCaptor.forClass(ConstraintLayout.LayoutParams.class);
- verify(mAppWidgetOverlayProviderFactory, times(1)).build(eq(mAppOverlayComponent2),
+ verify(mAppWidgetComplicationProviderFactory, times(1))
+ .build(eq(mAppComplicationComponent2),
layoutParamsArgumentCaptor.capture());
assertEquals(layoutParamsArgumentCaptor.getValue().endToEnd,
@@ -144,19 +147,19 @@ public class AppWidgetOverlayPrimerTest extends SysuiTestCase {
assertEquals(layoutParamsArgumentCaptor.getValue().bottomToBottom,
ConstraintLayout.LayoutParams.PARENT_ID);
verify(mDreamOverlayStateController, times(1))
- .addOverlay(eq(mAppWidgetOverlayProvider1));
+ .addComplication(eq(mComplicationProvider1));
}
}
@Test
public void testNoComponents() {
- when(mResources.getStringArray(R.array.config_dreamOverlayComponents))
+ when(mResources.getStringArray(R.array.config_dreamAppWidgetComplications))
.thenReturn(new String[]{});
- final AppWidgetOverlayPrimer primer = new AppWidgetOverlayPrimer(mContext,
+ final ComplicationPrimer primer = new ComplicationPrimer(mContext,
mResources,
mDreamOverlayStateController,
- mAppWidgetOverlayProviderFactory);
+ mAppWidgetComplicationProviderFactory);
// Inform primer to begin.
primer.onBootCompleted();
@@ -164,25 +167,25 @@ public class AppWidgetOverlayPrimerTest extends SysuiTestCase {
// Make sure there is no request to add a widget if no components are specified by the
// product.
- verify(mAppWidgetOverlayProviderFactory, never()).build(any(), any());
- verify(mDreamOverlayStateController, never()).addOverlay(any());
+ verify(mAppWidgetComplicationProviderFactory, never()).build(any(), any());
+ verify(mDreamOverlayStateController, never()).addComplication(any());
}
@Test
public void testNoPositions() {
- when(mResources.getIntArray(R.array.config_dreamOverlayPositions))
+ when(mResources.getIntArray(R.array.config_dreamComplicationPositions))
.thenReturn(new int[]{});
- final AppWidgetOverlayPrimer primer = new AppWidgetOverlayPrimer(mContext,
+ final ComplicationPrimer primer = new ComplicationPrimer(mContext,
mResources,
mDreamOverlayStateController,
- mAppWidgetOverlayProviderFactory);
+ mAppWidgetComplicationProviderFactory);
primer.onBootCompleted();
// Make sure there is no request to add a widget if no positions are specified by the
// product.
- verify(mAppWidgetOverlayProviderFactory, never()).build(any(), any());
- verify(mDreamOverlayStateController, never()).addOverlay(any());
+ verify(mAppWidgetComplicationProviderFactory, never()).build(any(), any());
+ verify(mDreamOverlayStateController, never()).addComplication(any());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
new file mode 100644
index 000000000000..f3043e934c8a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -0,0 +1,134 @@
+package com.android.systemui.keyguard
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration
+import android.graphics.Point
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.SyncRtSurfaceTransactionApplier
+import android.view.ViewRootImpl
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardViewController
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
+import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor.forClass
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
+ private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
+
+ @Mock
+ private lateinit var keyguardViewMediator: KeyguardViewMediator
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ private lateinit var keyguardViewController: KeyguardViewController
+ @Mock
+ private lateinit var smartspaceTransitionController: SmartspaceTransitionController
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var biometricUnlockController: BiometricUnlockController
+ @Mock
+ private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
+
+ private lateinit var remoteAnimationTarget: RemoteAnimationTarget
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ keyguardUnlockAnimationController = KeyguardUnlockAnimationController(
+ context, keyguardStateController, { keyguardViewMediator }, keyguardViewController,
+ smartspaceTransitionController, featureFlags, biometricUnlockController
+ )
+
+ `when`(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
+
+ // All of these fields are final, so we can't mock them, but are needed so that the surface
+ // appear amount setter doesn't short circuit.
+ remoteAnimationTarget = RemoteAnimationTarget(
+ 0, 0, null, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
+ mock(WindowConfiguration::class.java), false, mock(SurfaceControl::class.java), Rect(),
+ mock(ActivityManager.RunningTaskInfo::class.java), false)
+
+ // Set the surface applier to our mock so that we can verify the arguments passed to it.
+ // This applier does not have any side effects within the unlock animation controller, so
+ // this is a reasonable way to test.
+ keyguardUnlockAnimationController.surfaceTransactionApplier = surfaceTransactionApplier
+ }
+
+ /**
+ * If we're wake and unlocking, we are animating from the black/AOD screen to the app/launcher
+ * underneath. The LightRevealScrim will animate circularly from the fingerprint reader,
+ * revealing the app/launcher below. In this case, we want to make sure we are not animating the
+ * surface, or the user will see the wallpaper briefly as the app animates in.
+ */
+ @Test
+ fun noSurfaceAnimation_ifWakeAndUnlocking() {
+ `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
+
+ keyguardUnlockAnimationController.notifyStartKeyguardExitAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java)
+ verify(surfaceTransactionApplier, times(1)).scheduleApply(captor.capture())
+
+ val params = captor.value
+
+ // We expect that we've instantly set the surface behind to alpha = 1f, and have no
+ // transforms (translate, scale) on its matrix.
+ assertEquals(params.alpha, 1f)
+ assertTrue(params.matrix.isIdentity)
+
+ // Also expect we've immediately asked the keyguard view mediator to finish the remote
+ // animation.
+ verify(keyguardViewMediator, times(1)).onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+
+ verifyNoMoreInteractions(surfaceTransactionApplier)
+ }
+
+ /**
+ * If we are not wake and unlocking, we expect the unlock animation to play normally.
+ */
+ @Test
+ fun surfaceAnimation_ifNotWakeAndUnlocking() {
+ `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(false)
+
+ keyguardUnlockAnimationController.notifyStartKeyguardExitAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ // Make sure the animator was started.
+ assertTrue(keyguardUnlockAnimationController.surfaceBehindEntryAnimator.isRunning)
+
+ // Since the animation is running, we should not have finished the remote animation.
+ verify(keyguardViewMediator, times(0)).onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index bf87a4a59c49..a3ffb2fe4b8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -22,6 +22,7 @@ import android.testing.TestableLooper
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.keyguard.WakefulnessLifecycle
@@ -82,6 +83,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock
private lateinit var configurationController: ConfigurationController
+ @Mock
+ private lateinit var uniqueObjectHostView: UniqueObjectHostView
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@Captor
@@ -94,6 +97,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
@Before
fun setup() {
+ context.getOrCreateTestableResources().addOverride(
+ R.bool.config_use_split_notification_shade, false)
mediaFrame = FrameLayout(context)
`when`(mediaCarouselController.mediaFrame).thenReturn(mediaFrame)
mediaHiearchyManager = MediaHierarchyManager(
@@ -124,7 +129,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
private fun setupHost(host: MediaHost, location: Int) {
`when`(host.location).thenReturn(location)
`when`(host.currentBounds).thenReturn(Rect())
- `when`(host.hostView).thenReturn(UniqueObjectHostView(context))
+ `when`(host.hostView).thenReturn(uniqueObjectHostView)
`when`(host.visible).thenReturn(true)
mediaHiearchyManager.register(host)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 1b5e5eba0c84..89c0712cdc7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -100,7 +100,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
public void getItemCount_zeroMode_containExtraOneForPairNew() {
when(mMediaOutputController.isZeroMode()).thenReturn(true);
- assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size() + 1);
+ assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size());
}
@Test
@@ -108,7 +108,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
when(mMediaOutputController.isZeroMode()).thenReturn(false);
- assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size() + 1);
+ assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size());
}
@Test
@@ -119,7 +119,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText(
R.string.media_output_dialog_pairing_new));
@@ -134,11 +133,10 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_SESSION_NAME);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
@@ -150,12 +148,10 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(mContext.getString(
- R.string.media_output_dialog_group));
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
@@ -172,26 +168,9 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
}
@Test
- public void onBindViewHolder_bindConnectedDevice_withSelectableDevice_showAddIcon() {
- when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(mMediaDevices);
- mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- public void onBindViewHolder_bindConnectedDevice_withoutSelectableDevice_hideAddIcon() {
- when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(new ArrayList<>());
- mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
- }
-
- @Test
public void onBindViewHolder_bindNonActiveConnectedDevice_verifyView() {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
- assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
@@ -206,7 +185,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
when(mMediaDevice2.isConnected()).thenReturn(false);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
- assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(
@@ -220,7 +198,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
- assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
index 2c883a78e009..cf6fd249ee33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
@@ -99,7 +99,6 @@ public class MediaOutputGroupAdapterTest extends SysuiTestCase {
assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(mContext.getText(
@@ -112,7 +111,6 @@ public class MediaOutputGroupAdapterTest extends SysuiTestCase {
assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -137,7 +135,6 @@ public class MediaOutputGroupAdapterTest extends SysuiTestCase {
assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -161,7 +158,6 @@ public class MediaOutputGroupAdapterTest extends SysuiTestCase {
assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -178,7 +174,6 @@ public class MediaOutputGroupAdapterTest extends SysuiTestCase {
assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
index bc0cff18d281..de6525a2e750 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
@@ -16,15 +16,21 @@
package com.android.systemui.media.taptotransfer
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
import android.view.View
import android.view.WindowManager
+import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.SettableFuture
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentCaptor
@@ -33,9 +39,15 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.util.concurrent.Future
@SmallTest
class MediaTttChipControllerTest : SysuiTestCase() {
+ private lateinit var appIconDrawable: Drawable
+ private lateinit var fakeMainClock: FakeSystemClock
+ private lateinit var fakeMainExecutor: FakeExecutor
+ private lateinit var fakeBackgroundClock: FakeSystemClock
+ private lateinit var fakeBackgroundExecutor: FakeExecutor
private lateinit var mediaTttChipController: MediaTttChipController
@@ -45,29 +57,36 @@ class MediaTttChipControllerTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- mediaTttChipController = MediaTttChipController(context, windowManager)
+ appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
+ fakeMainClock = FakeSystemClock()
+ fakeMainExecutor = FakeExecutor(fakeMainClock)
+ fakeBackgroundClock = FakeSystemClock()
+ fakeBackgroundExecutor = FakeExecutor(fakeBackgroundClock)
+ mediaTttChipController = MediaTttChipController(
+ context, windowManager, fakeMainExecutor, fakeBackgroundExecutor
+ )
}
@Test
fun displayChip_chipAdded() {
- mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
+ mediaTttChipController.displayChip(moveCloserToTransfer())
verify(windowManager).addView(any(), any())
}
@Test
fun displayChip_twice_chipNotAddedTwice() {
- mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
+ mediaTttChipController.displayChip(moveCloserToTransfer())
reset(windowManager)
- mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
+ mediaTttChipController.displayChip(moveCloserToTransfer())
verify(windowManager, never()).addView(any(), any())
}
@Test
fun removeChip_chipRemoved() {
// First, add the chip
- mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
+ mediaTttChipController.displayChip(moveCloserToTransfer())
// Then, remove it
mediaTttChipController.removeChip()
@@ -83,42 +102,113 @@ class MediaTttChipControllerTest : SysuiTestCase() {
}
@Test
- fun moveCloserToTransfer_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
- mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
+ fun moveCloserToTransfer_appIcon_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
+ mediaTttChipController.displayChip(moveCloserToTransfer())
val chipView = getChipView()
+ assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable)
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
- fun transferInitiated_chipTextContainsDeviceName_loadingIcon_noUndo() {
- mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+ fun transferInitiated_futureNotResolvedYet_appIcon_loadingIcon_noUndo() {
+ val future: SettableFuture<Runnable?> = SettableFuture.create()
+ mediaTttChipController.displayChip(transferInitiated(future))
+ // Don't resolve the future in any way and don't run our executors
+
+ // Assert we're still in the loading state
val chipView = getChipView()
+ assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable)
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
- fun transferSucceededNullUndoRunnable_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
- mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME, undoRunnable = null))
+ fun transferInitiated_futureResolvedSuccessfully_switchesToTransferSucceeded() {
+ val future: SettableFuture<Runnable?> = SettableFuture.create()
+ val undoRunnable = Runnable { }
+
+ mediaTttChipController.displayChip(transferInitiated(future))
+ future.set(undoRunnable)
+ fakeBackgroundExecutor.advanceClockToLast()
+ fakeBackgroundExecutor.runAllReady()
+ fakeMainExecutor.advanceClockToLast()
+ val numRun = fakeMainExecutor.runAllReady()
+
+ // Assert we ran the future callback
+ assertThat(numRun).isEqualTo(1)
+ // Assert that we've moved to the successful state
val chipView = getChipView()
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
- assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun transferInitiated_futureCancelled_chipRemoved() {
+ val future: SettableFuture<Runnable?> = SettableFuture.create()
+
+ mediaTttChipController.displayChip(transferInitiated(future))
+
+ future.cancel(true)
+ fakeBackgroundExecutor.advanceClockToLast()
+ fakeBackgroundExecutor.runAllReady()
+ fakeMainExecutor.advanceClockToLast()
+ val numRun = fakeMainExecutor.runAllReady()
+
+ // Assert we ran the future callback
+ assertThat(numRun).isEqualTo(1)
+ // Assert that we've hidden the chip
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
+ fun transferInitiated_futureNotResolvedAfterTimeout_chipRemoved() {
+ val future: SettableFuture<Runnable?> = SettableFuture.create()
+ mediaTttChipController.displayChip(transferInitiated(future))
+
+ // We won't set anything on the future, but we will still run the executors so that we're
+ // waiting on the future resolving. If we have a bug in our code, then this test will time
+ // out because we're waiting on the future indefinitely.
+ fakeBackgroundExecutor.advanceClockToLast()
+ fakeBackgroundExecutor.runAllReady()
+ fakeMainExecutor.advanceClockToLast()
+ val numRun = fakeMainExecutor.runAllReady()
+
+ // Assert we eventually decide to not wait for the future anymore
+ assertThat(numRun).isEqualTo(1)
+ // Assert we've hidden the chip
+ verify(windowManager).removeView(any())
}
@Test
- fun transferSucceededWithUndoRunnable_chipTextContainsDeviceName_noLoadingIcon_undoWithClick() {
- mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME) { })
+ fun transferSucceeded_appIcon_chipTextContainsDeviceName_noLoadingIcon() {
+ mediaTttChipController.displayChip(transferSucceeded())
val chipView = getChipView()
+ assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable)
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun transferSucceededNullUndoRunnable_noUndo() {
+ mediaTttChipController.displayChip(transferSucceeded(undoRunnable = null))
+
+ val chipView = getChipView()
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun transferSucceededWithUndoRunnable_undoWithClick() {
+ mediaTttChipController.displayChip(transferSucceeded { })
+
+ val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().hasOnClickListeners()).isTrue()
}
@@ -128,7 +218,7 @@ class MediaTttChipControllerTest : SysuiTestCase() {
var runnableRun = false
val runnable = Runnable { runnableRun = true }
- mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME, runnable))
+ mediaTttChipController.displayChip(transferSucceeded(undoRunnable = runnable))
getChipView().getUndoButton().performClick()
assertThat(runnableRun).isTrue()
@@ -136,36 +226,39 @@ class MediaTttChipControllerTest : SysuiTestCase() {
@Test
fun changeFromCloserToTransferToTransferInitiated_loadingIconAppears() {
- mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
- mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+ mediaTttChipController.displayChip(moveCloserToTransfer())
+ mediaTttChipController.displayChip(transferInitiated())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromTransferInitiatedToTransferSucceeded_loadingIconDisappears() {
- mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
- mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
+ mediaTttChipController.displayChip(transferInitiated())
+ mediaTttChipController.displayChip(transferSucceeded())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE)
}
@Test
fun changeFromTransferInitiatedToTransferSucceeded_undoButtonAppears() {
- mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
- mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME) { })
+ mediaTttChipController.displayChip(transferInitiated())
+ mediaTttChipController.displayChip(transferSucceeded { })
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromTransferSucceededToMoveCloser_undoButtonDisappears() {
- mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
- mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
+ mediaTttChipController.displayChip(transferSucceeded())
+ mediaTttChipController.displayChip(moveCloserToTransfer())
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
}
+ private fun LinearLayout.getAppIconDrawable(): Drawable =
+ (this.requireViewById<ImageView>(R.id.app_icon)).drawable
+
private fun LinearLayout.getChipText(): String =
(this.requireViewById<TextView>(R.id.text)).text as String
@@ -179,6 +272,22 @@ class MediaTttChipControllerTest : SysuiTestCase() {
verify(windowManager).addView(viewCaptor.capture(), any())
return viewCaptor.value as LinearLayout
}
+
+ /** Helper method providing default parameters to not clutter up the tests. */
+ private fun moveCloserToTransfer() = MoveCloserToTransfer(DEVICE_NAME, appIconDrawable)
+
+ /** Helper method providing default parameters to not clutter up the tests. */
+ private fun transferInitiated(
+ future: Future<Runnable?> = TEST_FUTURE
+ ) = TransferInitiated(DEVICE_NAME, appIconDrawable, future)
+
+ /** Helper method providing default parameters to not clutter up the tests. */
+ private fun transferSucceeded(
+ undoRunnable: Runnable? = null
+ ) = TransferSucceeded(DEVICE_NAME, appIconDrawable, undoRunnable)
}
private const val DEVICE_NAME = "My Tablet"
+// Use a settable future that hasn't yet been set so that we don't immediately switch to the success
+// state.
+private val TEST_FUTURE: SettableFuture<Runnable?> = SettableFuture.create()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 4b85fa9fb75c..91b529875654 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -20,7 +20,9 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
@@ -46,7 +48,9 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
mediaTttCommandLineHelper =
- MediaTttCommandLineHelper(commandRegistry, mediaTttChipController)
+ MediaTttCommandLineHelper(
+ commandRegistry, context, mediaTttChipController, FakeExecutor(FakeSystemClock())
+ )
}
@Test(expected = IllegalStateException::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 6f4e61911924..8b353d94e25d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -42,7 +42,6 @@ import com.android.systemui.Dependency;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -54,7 +53,6 @@ import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.connectivity.StatusBarFlags;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -100,10 +98,6 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
@Mock
private TileServiceRequestController mTileServiceRequestController;
- @Mock
- private FeatureFlags mFeatureFlags;
- @Mock
- private StatusBarFlags mStatusBarFlags;
public QSFragmentTest() {
super(QSFragment.class);
@@ -149,7 +143,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
mock(SecureSettings.class), mock(CustomTileStatePersister.class),
- mTileServiceRequestControllerBuilder, mFeatureFlags, mStatusBarFlags);
+ mTileServiceRequestControllerBuilder);
qs.setHost(host);
qs.setListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 913b1d74b76a..1e651bef318b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -63,7 +63,6 @@ import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.connectivity.StatusBarFlags;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -128,10 +127,6 @@ public class QSTileHostTest extends SysuiTestCase {
private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
@Mock
private TileServiceRequestController mTileServiceRequestController;
- @Mock
- private FeatureFlags mFeatureFlags;
- @Mock
- private StatusBarFlags mStatusBarFlags;
private Handler mHandler;
private TestableLooper mLooper;
@@ -151,10 +146,8 @@ public class QSTileHostTest extends SysuiTestCase {
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
- mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder,
- mFeatureFlags, mStatusBarFlags);
+ mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder);
setUpTileFactory();
- when(mStatusBarFlags.isProviderModelSettingEnabled()).thenReturn(false);
}
private void setUpTileFactory() {
@@ -182,13 +175,13 @@ public class QSTileHostTest extends SysuiTestCase {
@Test
public void testLoadTileSpecs_emptySetting() {
- List<String> tiles = QSTileHost.loadTileSpecs(mContext, "", mStatusBarFlags);
+ List<String> tiles = QSTileHost.loadTileSpecs(mContext, "");
assertFalse(tiles.isEmpty());
}
@Test
public void testLoadTileSpecs_nullSetting() {
- List<String> tiles = QSTileHost.loadTileSpecs(mContext, null, mStatusBarFlags);
+ List<String> tiles = QSTileHost.loadTileSpecs(mContext, null);
assertFalse(tiles.isEmpty());
}
@@ -203,7 +196,6 @@ public class QSTileHostTest extends SysuiTestCase {
@Test
public void testRemoveWifiAndCellularWithoutInternet() {
- when(mStatusBarFlags.isProviderModelSettingEnabled()).thenReturn(true);
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2");
assertEquals("internet", mQSTileHost.mTileSpecs.get(0));
@@ -213,7 +205,6 @@ public class QSTileHostTest extends SysuiTestCase {
@Test
public void testRemoveWifiAndCellularWithInternet() {
- when(mStatusBarFlags.isProviderModelSettingEnabled()).thenReturn(true);
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2, internet");
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
@@ -223,7 +214,6 @@ public class QSTileHostTest extends SysuiTestCase {
@Test
public void testRemoveWifiWithoutInternet() {
- when(mStatusBarFlags.isProviderModelSettingEnabled()).thenReturn(true);
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, wifi, spec2");
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
@@ -233,7 +223,6 @@ public class QSTileHostTest extends SysuiTestCase {
@Test
public void testRemoveCellWithInternet() {
- when(mStatusBarFlags.isProviderModelSettingEnabled()).thenReturn(true);
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, spec2, cell, internet");
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
@@ -243,7 +232,6 @@ public class QSTileHostTest extends SysuiTestCase {
@Test
public void testNoWifiNoCellularNoInternet() {
- when(mStatusBarFlags.isProviderModelSettingEnabled()).thenReturn(true);
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
@@ -383,7 +371,7 @@ public class QSTileHostTest extends SysuiTestCase {
@Test
public void testLoadTileSpec_repeated() {
- List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2", mStatusBarFlags);
+ List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2");
assertEquals(2, specs.size());
assertEquals("spec1", specs.get(0));
@@ -394,7 +382,7 @@ public class QSTileHostTest extends SysuiTestCase {
public void testLoadTileSpec_repeatedInDefault() {
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles_default, "spec1,spec1");
- List<String> specs = QSTileHost.loadTileSpecs(mContext, "default", mStatusBarFlags);
+ List<String> specs = QSTileHost.loadTileSpecs(mContext, "default");
// Remove spurious tiles, like dbg:mem
specs.removeIf(spec -> !"spec1".equals(spec));
@@ -405,7 +393,7 @@ public class QSTileHostTest extends SysuiTestCase {
public void testLoadTileSpec_repeatedDefaultAndSetting() {
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles_default, "spec1");
- List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1", mStatusBarFlags);
+ List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1");
// Remove spurious tiles, like dbg:mem
specs.removeIf(spec -> !"spec1".equals(spec));
@@ -444,13 +432,11 @@ public class QSTileHostTest extends SysuiTestCase {
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
UiEventLogger uiEventLogger, UserTracker userTracker,
SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister,
- TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
- FeatureFlags featureFlags, StatusBarFlags statusBarFlags) {
+ TileServiceRequestController.Builder tileServiceRequestControllerBuilder) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpManager, broadcastDispatcher,
Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings,
- customTileStatePersister, tileServiceRequestControllerBuilder, featureFlags,
- statusBarFlags);
+ customTileStatePersister, tileServiceRequestControllerBuilder);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
index 93c75ad83afc..5212255078fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -53,13 +53,8 @@ public class QSCarrierTest extends SysuiTestCase {
mTestableLooper.runWithLooper(() ->
mQSCarrier = (QSCarrier) inflater.inflate(R.layout.qs_carrier, null));
- if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- // In this case, the id is an actual drawable id
- mSignalIconId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
- } else {
- // In this case, the id is a level
- mSignalIconId = SignalDrawable.getEmptyState(5);
- }
+ // In this case, the id is an actual drawable id
+ mSignalIconId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index c3a488ff6569..8b7346dd9e6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -58,7 +58,6 @@ import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.connectivity.StatusBarFlags;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -82,12 +81,12 @@ import java.util.concurrent.Executor;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class TileQueryHelperTest extends SysuiTestCase {
- private static final String CURRENT_TILES = "wifi,dnd,nfc";
- private static final String ONLY_STOCK_TILES = "wifi,dnd";
- private static final String WITH_OTHER_TILES = "wifi,dnd,other";
+ private static final String CURRENT_TILES = "internet,dnd,nfc";
+ private static final String ONLY_STOCK_TILES = "internet,dnd";
+ private static final String WITH_OTHER_TILES = "internet,dnd,other";
// Note no nfc in stock tiles
- private static final String STOCK_TILES = "wifi,dnd,cell,battery";
- private static final String ALL_TILES = "wifi,dnd,nfc,cell,battery";
+ private static final String STOCK_TILES = "internet,dnd,battery";
+ private static final String ALL_TILES = "internet,dnd,nfc,battery";
private static final Set<String> FACTORY_TILES = new ArraySet<>();
private static final String TEST_PKG = "test_pkg";
private static final String TEST_CLS = "test_cls";
@@ -95,7 +94,7 @@ public class TileQueryHelperTest extends SysuiTestCase {
static {
FACTORY_TILES.addAll(Arrays.asList(
- new String[]{"wifi", "bt", "cell", "dnd", "inversion", "airplane", "work",
+ new String[]{"internet", "bt", "dnd", "inversion", "airplane", "work",
"rotation", "flashlight", "location", "cast", "hotspot", "user", "battery",
"saver", "night", "nfc"}));
FACTORY_TILES.add(CUSTOM_TILE);
@@ -109,8 +108,6 @@ public class TileQueryHelperTest extends SysuiTestCase {
private PackageManager mPackageManager;
@Mock
private UserTracker mUserTracker;
- @Mock
- private StatusBarFlags mStatusBarFlags;
@Captor
private ArgumentCaptor<List<TileQueryHelper.TileInfo>> mCaptor;
@@ -136,12 +133,11 @@ public class TileQueryHelperTest extends SysuiTestCase {
}
}
).when(mQSTileHost).createTile(anyString());
- when(mStatusBarFlags.isProviderModelSettingEnabled()).thenReturn(false);
FakeSystemClock clock = new FakeSystemClock();
mMainExecutor = new FakeExecutor(clock);
mBgExecutor = new FakeExecutor(clock);
mTileQueryHelper = new TileQueryHelper(
- mContext, mUserTracker, mMainExecutor, mBgExecutor, mStatusBarFlags);
+ mContext, mUserTracker, mMainExecutor, mBgExecutor);
mTileQueryHelper.setListener(mListener);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 29b3b86018bb..d604b2cdbad8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -45,13 +45,11 @@ import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.connectivity.StatusBarFlags;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -106,10 +104,6 @@ public class TileServicesTest extends SysuiTestCase {
private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
@Mock
private TileServiceRequestController mTileServiceRequestController;
- @Mock
- private FeatureFlags mFeatureFlags;
- @Mock
- private StatusBarFlags mStatusBarFlags;
@Before
public void setUp() throws Exception {
@@ -136,9 +130,7 @@ public class TileServicesTest extends SysuiTestCase {
mUserTracker,
mSecureSettings,
mock(CustomTileStatePersister.class),
- mTileServiceRequestControllerBuilder,
- mFeatureFlags,
- mStatusBarFlags);
+ mTileServiceRequestControllerBuilder);
mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
mUserTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index ee6324b011cd..b31dd3c155f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -126,7 +126,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
protected CarrierConfigTracker mCarrierConfigTracker;
protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
protected FeatureFlags mFeatureFlags;
- protected StatusBarFlags mStatusBarFlags;
protected int mSubId;
@@ -157,10 +156,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
mFeatureFlags = mock(FeatureFlags.class);
- mStatusBarFlags = mock(StatusBarFlags.class);
when(mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false);
- when(mStatusBarFlags.isProviderModelSettingEnabled()).thenReturn(true);
-
mInstrumentation = InstrumentationRegistry.getInstrumentation();
Settings.Global.putInt(mContext.getContentResolver(), Global.AIRPLANE_MODE_ON, 0);
@@ -238,7 +234,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mDemoModeController,
mCarrierConfigTracker,
mFeatureFlags,
- mStatusBarFlags,
mock(DumpManager.class)
);
setupNetworkController();
@@ -308,7 +303,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mock(AccessPointControllerImpl.class),
mock(DataUsageController.class), mMockSubDefaults,
mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
- mCarrierConfigTracker, mFeatureFlags, mStatusBarFlags,
+ mCarrierConfigTracker, mFeatureFlags,
mock(DumpManager.class));
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index 0ed4243ef9de..138881ab1b66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -130,7 +130,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
mock(AccessPointControllerImpl.class),
mock(DataUsageController.class), mMockSubDefaults,
mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
- mock(CarrierConfigTracker.class), mFeatureFlags, mStatusBarFlags,
+ mock(CarrierConfigTracker.class), mFeatureFlags,
mock(DumpManager.class));
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 64da14179f7e..73eddd166f88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -70,7 +70,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags,
- mStatusBarFlags, mock(DumpManager.class));
+ mock(DumpManager.class));
setupNetworkController();
verifyLastMobileDataIndicators(false, -1, 0);
@@ -91,7 +91,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags,
- mStatusBarFlags, mock(DumpManager.class));
+ mock(DumpManager.class));
mNetworkController.registerListeners();
// Wait for the main looper to execute the previous command
@@ -160,7 +160,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags,
- mStatusBarFlags, mock(DumpManager.class));
+ mock(DumpManager.class));
setupNetworkController();
// No Subscriptions.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index de627de2e1d0..1961ab269267 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -214,6 +214,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
// THEN the session is created
verify(smartspaceManager).createSmartspaceSession(any())
+ // THEN an event notifier is registered
+ verify(plugin).registerSmartspaceEventNotifier(any())
}
@Test
@@ -241,7 +243,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
}
@Test
- fun testEmptyListIsEmittedAfterDisconnect() {
+ fun testEmptyListIsEmittedAndNotifierRemovedAfterDisconnect() {
// GIVEN a registered listener on an active session
connectSession()
clearInvocations(plugin)
@@ -250,8 +252,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
controller.stateChangeListener.onViewDetachedFromWindow(smartspaceView as View)
controller.disconnect()
- // THEN the listener receives an empty list of targets
+ // THEN the listener receives an empty list of targets and unregisters the notifier
verify(plugin).onTargetsAvailable(emptyList())
+ verify(plugin).registerSmartspaceEventNotifier(null)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 9be283716058..eda0e83d7eb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -22,6 +22,7 @@ import static android.view.View.GONE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static junit.framework.Assert.assertEquals;
@@ -102,6 +103,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
@Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
@Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ @Mock private NotificationShelf mNotificationShelf;
@Before
@UiThreadTest
@@ -123,13 +125,13 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mDependency.injectTestDependency(GroupMembershipManager.class, mGroupMembershipManger);
mDependency.injectTestDependency(GroupExpansionManager.class, mGroupExpansionManager);
mDependency.injectTestDependency(AmbientState.class, mAmbientState);
+ mDependency.injectTestDependency(NotificationShelf.class, mNotificationShelf);
mDependency.injectTestDependency(
UnlockedScreenOffAnimationController.class, mUnlockedScreenOffAnimationController);
NotificationShelfController notificationShelfController =
mock(NotificationShelfController.class);
- NotificationShelf notificationShelf = mock(NotificationShelf.class);
- when(notificationShelfController.getView()).thenReturn(notificationShelf);
+ when(notificationShelfController.getView()).thenReturn(mNotificationShelf);
when(mNotificationSectionsManager.createSectionsForBuckets()).thenReturn(
new NotificationSection[]{
mNotificationSection
@@ -159,7 +161,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
anyBoolean());
doNothing().when(mGroupExpansionManager).collapseGroups();
doNothing().when(mExpandHelper).cancelImmediately();
- doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
+ doNothing().when(mNotificationShelf).setAnimationsEnabled(anyBoolean());
}
@Test
@@ -202,21 +204,43 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
@UiThreadTest
- public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
- final float[] expectedHeight = {0f};
- final float[] expectedAppear = {0f};
+ public void testSetExpandedHeight_listenerReceivedCallbacks() {
+ final float expectedHeight = 0f;
mStackScroller.addOnExpandedHeightChangedListener((height, appear) -> {
- Assert.assertEquals(expectedHeight[0], height, 0);
- Assert.assertEquals(expectedAppear[0], appear, .1);
+ Assert.assertEquals(expectedHeight, height, 0);
});
- expectedHeight[0] = 1f;
- expectedAppear[0] = 1f;
- mStackScroller.setExpandedHeight(expectedHeight[0]);
+ mStackScroller.setExpandedHeight(expectedHeight);
+ }
- expectedHeight[0] = 100f;
- expectedAppear[0] = 0f;
- mStackScroller.setExpandedHeight(expectedHeight[0]);
+ @Test
+ public void testAppearFractionCalculation() {
+ // appear start position
+ when(mNotificationShelf.getIntrinsicHeight()).thenReturn(100);
+ // because it's the same as shelf height, appear start position equals shelf height
+ mStackScroller.mStatusBarHeight = 100;
+ // appear end position
+ when(mEmptyShadeView.getHeight()).thenReturn(200);
+
+ assertEquals(0f, mStackScroller.calculateAppearFraction(100));
+ assertEquals(1f, mStackScroller.calculateAppearFraction(200));
+ assertEquals(0.5f, mStackScroller.calculateAppearFraction(150));
+ }
+
+ @Test
+ public void testAppearFractionCalculationIsNotNegativeWhenShelfBecomesSmaller() {
+ // this situation might occur if status bar height is defined in pixels while shelf height
+ // in dp and screen density changes - appear start position
+ // (calculated in NSSL#getMinExpansionHeight) that is adjusting for status bar might
+ // increase and become bigger that end position, which should be prevented
+
+ // appear start position
+ when(mNotificationShelf.getIntrinsicHeight()).thenReturn(80);
+ mStackScroller.mStatusBarHeight = 100;
+ // appear end position
+ when(mEmptyShadeView.getHeight()).thenReturn(90);
+
+ assertThat(mStackScroller.calculateAppearFraction(100)).isAtLeast(0);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index e4db07220949..a14ea54fc7e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -17,11 +17,12 @@
package com.android.systemui.statusbar.phone;
import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
import android.content.res.Resources;
@@ -32,14 +33,19 @@ import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import org.junit.Assert;
import org.junit.Before;
@@ -48,10 +54,11 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DozeParametersTest extends SysuiTestCase {
-
private DozeParameters mDozeParameters;
@Mock Resources mResources;
@@ -63,10 +70,37 @@ public class DozeParametersTest extends SysuiTestCase {
@Mock private FeatureFlags mFeatureFlags;
@Mock private DumpManager mDumpManager;
@Mock private ScreenOffAnimationController mScreenOffAnimationController;
+ @Mock private FoldAodAnimationController mFoldAodAnimationController;
+ @Mock private SysUIUnfoldComponent mSysUIUnfoldComponent;
+ @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock private StatusBarStateController mStatusBarStateController;
+ @Mock private ConfigurationController mConfigurationController;
+
+ /**
+ * The current value of PowerManager's dozeAfterScreenOff property.
+ *
+ * This property controls whether System UI is controlling the screen off animation. If it's
+ * false (PowerManager should not doze after screen off) then System UI is controlling the
+ * animation. If true, we're not controlling it and PowerManager will doze immediately.
+ */
+ private boolean mPowerManagerDozeAfterScreenOff;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+
+ // Save the current value set for dozeAfterScreenOff so we can make assertions. This method
+ // is only called if the value changes, which makes it difficult to check that it was set
+ // correctly in tests.
+ doAnswer(invocation -> {
+ mPowerManagerDozeAfterScreenOff = invocation.getArgument(0);
+ return mPowerManagerDozeAfterScreenOff;
+ }).when(mPowerManager).setDozeAfterScreenOff(anyBoolean());
+
+ when(mSysUIUnfoldComponent.getFoldAodAnimationController())
+ .thenReturn(mFoldAodAnimationController);
+
mDozeParameters = new DozeParameters(
mResources,
mAmbientDisplayConfiguration,
@@ -76,23 +110,31 @@ public class DozeParametersTest extends SysuiTestCase {
mTunerService,
mDumpManager,
mFeatureFlags,
- mScreenOffAnimationController
+ mScreenOffAnimationController,
+ Optional.of(mSysUIUnfoldComponent),
+ mUnlockedScreenOffAnimationController,
+ mKeyguardUpdateMonitor,
+ mConfigurationController,
+ mStatusBarStateController
);
+
+ when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)).thenReturn(true);
+ setAodEnabledForTest(true);
+ setShouldControlUnlockedScreenOffForTest(true);
+ setDisplayNeedsBlankingForTest(false);
}
+
@Test
- public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
+ public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_correctly() {
+ // If we want to control screen off, we do NOT want PowerManager to doze after screen off.
+ // Obviously.
mDozeParameters.setControlScreenOffAnimation(true);
- reset(mPowerManager);
- mDozeParameters.setControlScreenOffAnimation(false);
- verify(mPowerManager).setDozeAfterScreenOff(eq(true));
- }
+ assertFalse(mPowerManagerDozeAfterScreenOff);
- @Test
- public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
+ // If we don't want to control screen off, PowerManager is free to doze after screen off if
+ // that's what'll make it happy.
mDozeParameters.setControlScreenOffAnimation(false);
- reset(mPowerManager);
- mDozeParameters.setControlScreenOffAnimation(true);
- verify(mPowerManager).setDozeAfterScreenOff(eq(false));
+ assertTrue(mPowerManagerDozeAfterScreenOff);
}
@Test
@@ -121,35 +163,124 @@ public class DozeParametersTest extends SysuiTestCase {
assertThat(mDozeParameters.getAlwaysOn()).isFalse();
}
+ /**
+ * PowerManager.setDozeAfterScreenOff(true) means we are not controlling screen off, and calling
+ * it with false means we are. Confusing, but sure - make sure that we call PowerManager with
+ * the correct value depending on whether we want to control screen off.
+ */
@Test
public void testControlUnlockedScreenOffAnimation_dozeAfterScreenOff_false() {
- when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
- mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
- when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)).thenReturn(true);
- when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
+ // If AOD is disabled, we shouldn't want to control screen off. Also, let's double check
+ // that when that value is updated, we called through to PowerManager.
+ setAodEnabledForTest(false);
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ assertTrue(mPowerManagerDozeAfterScreenOff);
- // Trigger the setter for the current value.
- mDozeParameters.setControlScreenOffAnimation(mDozeParameters.shouldControlScreenOff());
-
- // We should have asked power manager not to doze after screen off no matter what, since
- // we're animating and controlling screen off.
- verify(mPowerManager).setDozeAfterScreenOff(eq(false));
+ // And vice versa...
+ setAodEnabledForTest(true);
+ assertTrue(mDozeParameters.shouldControlScreenOff());
+ assertFalse(mPowerManagerDozeAfterScreenOff);
}
@Test
public void testControlUnlockedScreenOffAnimationDisabled_dozeAfterScreenOff() {
- when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
- mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+ setShouldControlUnlockedScreenOffForTest(true);
when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)).thenReturn(false);
assertFalse(mDozeParameters.shouldControlUnlockedScreenOff());
// Trigger the setter for the current value.
mDozeParameters.setControlScreenOffAnimation(mDozeParameters.shouldControlScreenOff());
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ }
+
+ @Test
+ public void propagatesAnimateScreenOff_noAlwaysOn() {
+ setAodEnabledForTest(false);
+ setDisplayNeedsBlankingForTest(false);
+
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(false);
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ }
+
+ @Test
+ public void propagatesAnimateScreenOff_alwaysOn() {
+ setAodEnabledForTest(true);
+ setDisplayNeedsBlankingForTest(false);
+ setShouldControlUnlockedScreenOffForTest(false);
+
+ // Take over when the keyguard is visible.
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(true);
+ assertTrue(mDozeParameters.shouldControlScreenOff());
+
+ // Do not animate screen-off when keyguard isn't visible.
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(false);
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ }
+
+
+ @Test
+ public void neverAnimateScreenOff_whenNotSupported() {
+ setDisplayNeedsBlankingForTest(true);
+
+ // Never animate if display doesn't support it.
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(true);
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(false);
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ }
+
+
+ @Test
+ public void controlScreenOffTrueWhenKeyguardNotShowingAndControlUnlockedScreenOff() {
+ setShouldControlUnlockedScreenOffForTest(true);
+
+ // Tell doze that keyguard is not visible.
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(
+ false /* showing */);
+
+ // Since we're controlling the unlocked screen off animation, verify that we've asked to
+ // control the screen off animation despite being unlocked.
+ assertTrue(mDozeParameters.shouldControlScreenOff());
+ }
+
+
+ @Test
+ public void keyguardVisibility_changesControlScreenOffAnimation() {
+ setShouldControlUnlockedScreenOffForTest(false);
+
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(false);
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(true);
+ assertTrue(mDozeParameters.shouldControlScreenOff());
+ }
+
+ @Test
+ public void keyguardVisibility_changesControlScreenOffAnimation_respectsUnlockedScreenOff() {
+ setShouldControlUnlockedScreenOffForTest(true);
+
+ // Even if the keyguard is gone, we should control screen off if we can control unlocked
+ // screen off.
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(false);
+ assertTrue(mDozeParameters.shouldControlScreenOff());
+
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(true);
+ assertTrue(mDozeParameters.shouldControlScreenOff());
+ }
+
+ private void setDisplayNeedsBlankingForTest(boolean needsBlanking) {
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_displayBlanksAfterDoze)).thenReturn(
+ needsBlanking);
+ }
+
+ private void setAodEnabledForTest(boolean enabled) {
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(enabled);
+ mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "");
+ }
- // We should have asked power manager to doze only if we're not controlling screen off
- // normally.
- verify(mPowerManager).setDozeAfterScreenOff(
- eq(!mDozeParameters.shouldControlScreenOff()));
+ private void setShouldControlUnlockedScreenOffForTest(boolean shouldControl) {
+ when(mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation())
+ .thenReturn(shouldControl);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 8d05e6693e33..37cf7485b8ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -278,11 +278,11 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
}
@Test
- public void updateViewState_qsExpansionOne_viewHidden() {
+ public void updateViewState_dragProgressOne_viewHidden() {
mController.onViewAttached();
updateStateToKeyguard();
- mNotificationPanelViewStateProvider.setQsExpansionFraction(1f);
+ mNotificationPanelViewStateProvider.setLockscreenShadeDragProgress(1f);
mController.updateViewState();
@@ -356,6 +356,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
private float mPanelViewExpandedHeight = 100f;
private float mQsExpansionFraction = 0f;
private boolean mShouldHeadsUpBeVisible = false;
+ private float mLockscreenShadeDragProgress = 0f;
@Override
public float getPanelViewExpandedHeight() {
@@ -372,6 +373,11 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
return mShouldHeadsUpBeVisible;
}
+ @Override
+ public float getLockscreenShadeDragProgress() {
+ return mLockscreenShadeDragProgress;
+ }
+
public void setPanelViewExpandedHeight(float panelViewExpandedHeight) {
this.mPanelViewExpandedHeight = panelViewExpandedHeight;
}
@@ -383,5 +389,9 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
public void setShouldHeadsUpBeVisible(boolean shouldHeadsUpBeVisible) {
this.mShouldHeadsUpBeVisible = shouldHeadsUpBeVisible;
}
+
+ public void setLockscreenShadeDragProgress(float lockscreenShadeDragProgress) {
+ this.mLockscreenShadeDragProgress = lockscreenShadeDragProgress;
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
index a8544a9a15e4..2b1826eab5aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
@@ -8,6 +8,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.qs.HeaderPrivacyIconsController
@@ -37,6 +38,7 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() {
@Mock private lateinit var batteryMeterView: BatteryMeterView
@Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
@Mock private lateinit var privacyIconsController: HeaderPrivacyIconsController
+ @Mock private lateinit var dumpManager: DumpManager
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
@@ -66,7 +68,8 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() {
privacyIconsController,
qsCarrierGroupControllerBuilder,
featureFlags,
- batteryMeterViewController
+ batteryMeterViewController,
+ dumpManager
)
carrierIconSlots = listOf(
context.getString(com.android.internal.R.string.status_bar_mobile))
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml
index a03dcbd3941d..1f5834d72dd0 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml
@@ -18,5 +18,5 @@
-->
<resources>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">24dp</dimen>
+ <dimen name="navigation_bar_gesture_height">32dp</dimen>
</resources>
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml
index c5d0c9e3f348..ac1f0226be52 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml
@@ -18,13 +18,13 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">24dp</dimen>
+ <dimen name="navigation_bar_height">16dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
- <dimen name="navigation_bar_height_landscape">24dp</dimen>
+ <dimen name="navigation_bar_height_landscape">16dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">24dp</dimen>
+ <dimen name="navigation_bar_width">16dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_frame_height">48dp</dimen>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">24dp</dimen>
+ <dimen name="navigation_bar_gesture_height">32dp</dimen>
</resources> \ No newline at end of file
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml
index c5d0c9e3f348..ac1f0226be52 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml
@@ -18,13 +18,13 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">24dp</dimen>
+ <dimen name="navigation_bar_height">16dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
- <dimen name="navigation_bar_height_landscape">24dp</dimen>
+ <dimen name="navigation_bar_height_landscape">16dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">24dp</dimen>
+ <dimen name="navigation_bar_width">16dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_frame_height">48dp</dimen>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">24dp</dimen>
+ <dimen name="navigation_bar_gesture_height">32dp</dimen>
</resources> \ No newline at end of file
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml
index c5d0c9e3f348..ac1f0226be52 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml
@@ -18,13 +18,13 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">24dp</dimen>
+ <dimen name="navigation_bar_height">16dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
- <dimen name="navigation_bar_height_landscape">24dp</dimen>
+ <dimen name="navigation_bar_height_landscape">16dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">24dp</dimen>
+ <dimen name="navigation_bar_width">16dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_frame_height">48dp</dimen>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">24dp</dimen>
+ <dimen name="navigation_bar_gesture_height">32dp</dimen>
</resources> \ No newline at end of file
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml
index c5d0c9e3f348..ac1f0226be52 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml
@@ -18,13 +18,13 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">24dp</dimen>
+ <dimen name="navigation_bar_height">16dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
- <dimen name="navigation_bar_height_landscape">24dp</dimen>
+ <dimen name="navigation_bar_height_landscape">16dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">24dp</dimen>
+ <dimen name="navigation_bar_width">16dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_frame_height">48dp</dimen>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">24dp</dimen>
+ <dimen name="navigation_bar_gesture_height">32dp</dimen>
</resources> \ No newline at end of file
diff --git a/services/Android.bp b/services/Android.bp
index dcbcff0aa539..c830c226cb66 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -90,6 +90,7 @@ filegroup {
":services.profcollect-sources",
":services.restrictions-sources",
":services.searchui-sources",
+ ":services.selectiontoolbar-sources",
":services.smartspace-sources",
":services.speech-sources",
":services.startop.iorap-sources",
@@ -144,6 +145,7 @@ java_library {
"services.profcollect",
"services.restrictions",
"services.searchui",
+ "services.selectiontoolbar",
"services.smartspace",
"services.speech",
"services.startop",
diff --git a/services/OWNERS b/services/OWNERS
index a08331996556..67cee5517913 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -5,3 +5,5 @@ per-file art-profile* = calin@google.com, ngeoffray@google.com, vmarko@google.co
per-file java/com/android/server/* = toddke@google.com,patb@google.com
per-file tests/servicestests/src/com/android/server/systemconfig/* = patb@google.com
+
+per-file proguard.flags = jdduke@google.com
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
index 85ab48c5f7fb..89633373b152 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
@@ -17,6 +17,7 @@
package com.android.server.backup.transport;
import android.annotation.Nullable;
+import android.app.backup.BackupTransport;
import android.app.backup.RestoreDescription;
import android.app.backup.RestoreSet;
import android.content.Intent;
@@ -24,50 +25,70 @@ import android.content.pm.PackageInfo;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.backup.IBackupTransport;
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* Client to {@link com.android.internal.backup.IBackupTransport}. Manages the call to the remote
* transport service and delivers the results.
*/
public class BackupTransportClient {
+ private static final String TAG = "BackupTransportClient";
+
private final IBackupTransport mTransportBinder;
+ private final TransportStatusCallbackPool mCallbackPool;
BackupTransportClient(IBackupTransport transportBinder) {
mTransportBinder = transportBinder;
-
- // This is a temporary fix to allow blocking calls.
- // TODO: b/147702043. Redesign IBackupTransport so as to make the calls non-blocking.
- Binder.allowBlocking(mTransportBinder.asBinder());
+ mCallbackPool = new TransportStatusCallbackPool();
}
/**
* See {@link IBackupTransport#name()}.
*/
public String name() throws RemoteException {
- return mTransportBinder.name();
+ AndroidFuture<String> resultFuture = new AndroidFuture<>();
+ mTransportBinder.name(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
* See {@link IBackupTransport#configurationIntent()}
*/
public Intent configurationIntent() throws RemoteException {
- return mTransportBinder.configurationIntent();
+ AndroidFuture<Intent> resultFuture = new AndroidFuture<>();
+ mTransportBinder.configurationIntent(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
* See {@link IBackupTransport#currentDestinationString()}
*/
public String currentDestinationString() throws RemoteException {
- return mTransportBinder.currentDestinationString();
+ AndroidFuture<String> resultFuture = new AndroidFuture<>();
+ mTransportBinder.currentDestinationString(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
* See {@link IBackupTransport#dataManagementIntent()}
*/
public Intent dataManagementIntent() throws RemoteException {
- return mTransportBinder.dataManagementIntent();
+ AndroidFuture<Intent> resultFuture = new AndroidFuture<>();
+ mTransportBinder.dataManagementIntent(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
@@ -75,42 +96,67 @@ public class BackupTransportClient {
*/
@Nullable
public CharSequence dataManagementIntentLabel() throws RemoteException {
- return mTransportBinder.dataManagementIntentLabel();
+ AndroidFuture<CharSequence> resultFuture = new AndroidFuture<>();
+ mTransportBinder.dataManagementIntentLabel(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
* See {@link IBackupTransport#transportDirName()}
*/
public String transportDirName() throws RemoteException {
- return mTransportBinder.transportDirName();
+ AndroidFuture<String> resultFuture = new AndroidFuture<>();
+ mTransportBinder.transportDirName(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
* See {@link IBackupTransport#initializeDevice()}
*/
public int initializeDevice() throws RemoteException {
- return mTransportBinder.initializeDevice();
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.initializeDevice(callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#clearBackupData(PackageInfo)}
*/
public int clearBackupData(PackageInfo packageInfo) throws RemoteException {
- return mTransportBinder.clearBackupData(packageInfo);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.clearBackupData(packageInfo, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#finishBackup()}
*/
public int finishBackup() throws RemoteException {
- return mTransportBinder.finishBackup();
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.finishBackup(callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#requestBackupTime()}
*/
public long requestBackupTime() throws RemoteException {
- return mTransportBinder.requestBackupTime();
+ AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+ mTransportBinder.requestBackupTime(resultFuture);
+ Long result = getFutureResult(resultFuture);
+ return result == null ? BackupTransport.TRANSPORT_ERROR : result;
}
/**
@@ -118,56 +164,91 @@ public class BackupTransportClient {
*/
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)
throws RemoteException {
- return mTransportBinder.performBackup(packageInfo, inFd, flags);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.performBackup(packageInfo, inFd, flags, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#getAvailableRestoreSets()}
*/
public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
- return mTransportBinder.getAvailableRestoreSets();
+ AndroidFuture<List<RestoreSet>> resultFuture = new AndroidFuture<>();
+ mTransportBinder.getAvailableRestoreSets(resultFuture);
+ List<RestoreSet> result = getFutureResult(resultFuture);
+ return result == null ? null : result.toArray(new RestoreSet[] {});
}
/**
* See {@link IBackupTransport#getCurrentRestoreSet()}
*/
public long getCurrentRestoreSet() throws RemoteException {
- return mTransportBinder.getCurrentRestoreSet();
+ AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+ mTransportBinder.getCurrentRestoreSet(resultFuture);
+ Long result = getFutureResult(resultFuture);
+ return result == null ? BackupTransport.TRANSPORT_ERROR : result;
}
/**
* See {@link IBackupTransport#startRestore(long, PackageInfo[])}
*/
public int startRestore(long token, PackageInfo[] packages) throws RemoteException {
- return mTransportBinder.startRestore(token, packages);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.startRestore(token, packages, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#nextRestorePackage()}
*/
public RestoreDescription nextRestorePackage() throws RemoteException {
- return mTransportBinder.nextRestorePackage();
+ AndroidFuture<RestoreDescription> resultFuture = new AndroidFuture<>();
+ mTransportBinder.nextRestorePackage(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
* See {@link IBackupTransport#getRestoreData(ParcelFileDescriptor)}
*/
public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
- return mTransportBinder.getRestoreData(outFd);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.getRestoreData(outFd, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#finishRestore()}
*/
public void finishRestore() throws RemoteException {
- mTransportBinder.finishRestore();
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.finishRestore(callback);
+ callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#requestFullBackupTime()}
*/
public long requestFullBackupTime() throws RemoteException {
- return mTransportBinder.requestFullBackupTime();
+ AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+ mTransportBinder.requestFullBackupTime(resultFuture);
+ Long result = getFutureResult(resultFuture);
+ return result == null ? BackupTransport.TRANSPORT_ERROR : result;
}
/**
@@ -175,28 +256,52 @@ public class BackupTransportClient {
*/
public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket,
int flags) throws RemoteException {
- return mTransportBinder.performFullBackup(targetPackage, socket, flags);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.performFullBackup(targetPackage, socket, flags, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#checkFullBackupSize(long)}
*/
public int checkFullBackupSize(long size) throws RemoteException {
- return mTransportBinder.checkFullBackupSize(size);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.checkFullBackupSize(size, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#sendBackupData(int)}
*/
public int sendBackupData(int numBytes) throws RemoteException {
- return mTransportBinder.sendBackupData(numBytes);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ mTransportBinder.sendBackupData(numBytes, callback);
+ try {
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#cancelFullBackup()}
*/
public void cancelFullBackup() throws RemoteException {
- mTransportBinder.cancelFullBackup();
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.cancelFullBackup(callback);
+ callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
@@ -204,34 +309,93 @@ public class BackupTransportClient {
*/
public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup)
throws RemoteException {
- return mTransportBinder.isAppEligibleForBackup(targetPackage, isFullBackup);
+ AndroidFuture<Boolean> resultFuture = new AndroidFuture<>();
+ mTransportBinder.isAppEligibleForBackup(targetPackage, isFullBackup, resultFuture);
+ Boolean result = getFutureResult(resultFuture);
+ return result != null && result;
}
/**
* See {@link IBackupTransport#getBackupQuota(String, boolean)}
*/
public long getBackupQuota(String packageName, boolean isFullBackup) throws RemoteException {
- return mTransportBinder.getBackupQuota(packageName, isFullBackup);
+ AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+ mTransportBinder.getBackupQuota(packageName, isFullBackup, resultFuture);
+ Long result = getFutureResult(resultFuture);
+ return result == null ? BackupTransport.TRANSPORT_ERROR : result;
}
/**
* See {@link IBackupTransport#getNextFullRestoreDataChunk(ParcelFileDescriptor)}
*/
public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) throws RemoteException {
- return mTransportBinder.getNextFullRestoreDataChunk(socket);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.getNextFullRestoreDataChunk(socket, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#abortFullRestore()}
*/
public int abortFullRestore() throws RemoteException {
- return mTransportBinder.abortFullRestore();
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.abortFullRestore(callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#getTransportFlags()}
*/
public int getTransportFlags() throws RemoteException {
- return mTransportBinder.getTransportFlags();
+ AndroidFuture<Integer> resultFuture = new AndroidFuture<>();
+ mTransportBinder.getTransportFlags(resultFuture);
+ Integer result = getFutureResult(resultFuture);
+ return result == null ? BackupTransport.TRANSPORT_ERROR : result;
+ }
+
+ private <T> T getFutureResult(AndroidFuture<T> future) {
+ try {
+ return future.get(600, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Slog.w(TAG, "Failed to get result from transport:", e);
+ return null;
+ }
+ }
+
+ private static class TransportStatusCallbackPool {
+ private static final int MAX_POOL_SIZE = 100;
+
+ private final Object mPoolLock = new Object();
+ private final Queue<TransportStatusCallback> mCallbackPool = new ArrayDeque<>();
+
+ TransportStatusCallback acquire() {
+ synchronized (mPoolLock) {
+ if (mCallbackPool.isEmpty()) {
+ return new TransportStatusCallback();
+ } else {
+ return mCallbackPool.poll();
+ }
+ }
+ }
+
+ void recycle(TransportStatusCallback callback) {
+ synchronized (mPoolLock) {
+ if (mCallbackPool.size() > MAX_POOL_SIZE) {
+ Slog.d(TAG, "TransportStatusCallback pool size exceeded");
+ return;
+ }
+
+ callback.reset();
+ mCallbackPool.add(callback);
+ }
+ }
}
}
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
new file mode 100644
index 000000000000..a55178c27eef
--- /dev/null
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.backup.transport;
+
+import android.app.backup.BackupTransport;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.backup.ITransportStatusCallback;
+
+public class TransportStatusCallback extends ITransportStatusCallback.Stub {
+ private static final String TAG = "TransportStatusCallback";
+ private static final int TIMEOUT_MILLIS = 600 * 1000; // 10 minutes.
+ private static final int OPERATION_STATUS_DEFAULT = 0;
+
+ private final int mOperationTimeout;
+
+ @GuardedBy("this")
+ private int mOperationStatus = OPERATION_STATUS_DEFAULT;
+ @GuardedBy("this")
+ private boolean mHasCompletedOperation = false;
+
+ public TransportStatusCallback() {
+ mOperationTimeout = TIMEOUT_MILLIS;
+ }
+
+ @VisibleForTesting
+ TransportStatusCallback(int operationTimeout) {
+ mOperationTimeout = operationTimeout;
+ }
+
+ @Override
+ public synchronized void onOperationCompleteWithStatus(int status) throws RemoteException {
+ mHasCompletedOperation = true;
+ mOperationStatus = status;
+
+ notifyAll();
+ }
+
+ @Override
+ public synchronized void onOperationComplete() throws RemoteException {
+ onOperationCompleteWithStatus(OPERATION_STATUS_DEFAULT);
+ }
+
+ synchronized int getOperationStatus() {
+ if (mHasCompletedOperation) {
+ return mOperationStatus;
+ }
+
+ long timeoutLeft = mOperationTimeout;
+ try {
+ while (!mHasCompletedOperation && timeoutLeft > 0) {
+ long waitStartTime = System.currentTimeMillis();
+ wait(timeoutLeft);
+ if (mHasCompletedOperation) {
+ return mOperationStatus;
+ }
+ timeoutLeft -= System.currentTimeMillis() - waitStartTime;
+ }
+
+ Slog.w(TAG, "Couldn't get operation status from transport");
+ return BackupTransport.TRANSPORT_ERROR;
+ } catch (InterruptedException e) {
+ Slog.w(TAG, "Couldn't get operation status from transport: ", e);
+ return BackupTransport.TRANSPORT_ERROR;
+ } finally {
+ reset();
+ }
+ }
+
+ synchronized void reset() {
+ mHasCompletedOperation = false;
+ mOperationStatus = OPERATION_STATUS_DEFAULT;
+ }
+}
diff --git a/services/cloudsearch/OWNERS b/services/cloudsearch/OWNERS
new file mode 100644
index 000000000000..aa4da3b4bee0
--- /dev/null
+++ b/services/cloudsearch/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 758286
+
+huiwu@google.com
+srazdan@google.com
diff --git a/services/companion/java/com/android/server/companion/PermissionsUtils.java b/services/companion/java/com/android/server/companion/PermissionsUtils.java
index 3a8ee7398329..b981ff1d43c9 100644
--- a/services/companion/java/com/android/server/companion/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/PermissionsUtils.java
@@ -84,18 +84,6 @@ final class PermissionsUtils {
throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile);
}
- if (DEVICE_PROFILE_APP_STREAMING.equals(deviceProfile)) {
- // TODO: remove, when properly supporting this profile.
- throw new UnsupportedOperationException(
- "DEVICE_PROFILE_APP_STREAMING is not fully supported yet.");
- }
-
- if (DEVICE_PROFILE_AUTOMOTIVE_PROJECTION.equals(deviceProfile)) {
- // TODO: remove, when properly supporting this profile.
- throw new UnsupportedOperationException(
- "DEVICE_PROFILE_AUTOMOTIVE_PROJECTION is not fully supported yet.");
- }
-
final String permission = DEVICE_PROFILE_TO_PERMISSION.get(deviceProfile);
if (context.checkPermission(permission, getCallingPid(), packageUid)
!= PERMISSION_GRANTED) {
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index a6a87936616b..e98b63ecd4b5 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -29,6 +29,7 @@ import android.os.Build;
import android.os.UserHandle;
import android.window.DisplayWindowPolicyController;
+import java.util.HashSet;
import java.util.List;
@@ -45,6 +46,8 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;
+ @NonNull final HashSet<Integer> mRunningUids = new HashSet<>();
+
GenericWindowPolicyController(int windowFlags, int systemWindowFlags) {
setInterestedWindowFlags(windowFlags, systemWindowFlags);
}
@@ -89,6 +92,17 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {
@Override
public void onRunningAppsChanged(int[] runningUids) {
+ mRunningUids.clear();
+ for (int i = 0; i < runningUids.length; i++) {
+ mRunningUids.add(runningUids[i]);
+ }
+ }
+ /**
+ * Returns true if an app with the given UID has an activity running on the virtual display for
+ * this controller.
+ */
+ boolean containsUid(int uid) {
+ return mRunningUids.contains(uid);
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 022da4361be4..ca35e033ed70 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -32,6 +32,7 @@ import android.hardware.input.VirtualTouchEvent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.SparseArray;
import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.VisibleForTesting;
@@ -50,11 +51,18 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final Context mContext;
private final AssociationInfo mAssociationInfo;
private final int mOwnerUid;
- private final GenericWindowPolicyController mGenericWindowPolicyController;
private final InputController mInputController;
@VisibleForTesting
final List<Integer> mVirtualDisplayIds = new ArrayList<>();
private final OnDeviceCloseListener mListener;
+ private final IBinder mAppToken;
+
+ /**
+ * A mapping from the virtual display ID to its corresponding
+ * {@link GenericWindowPolicyController}.
+ */
+ private final SparseArray<GenericWindowPolicyController> mWindowPolicyControllers =
+ new SparseArray<>();
VirtualDeviceImpl(Context context, AssociationInfo associationInfo,
IBinder token, int ownerUid, OnDeviceCloseListener listener) {
@@ -66,9 +74,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
int ownerUid, InputController inputController, OnDeviceCloseListener listener) {
mContext = context;
mAssociationInfo = associationInfo;
- mGenericWindowPolicyController = new GenericWindowPolicyController(FLAG_SECURE,
- SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
mOwnerUid = ownerUid;
+ mAppToken = token;
if (inputController == null) {
mInputController = new InputController(mVirtualDeviceLock);
} else {
@@ -90,6 +97,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@Override // Binder call
public void close() {
mListener.onClose(mAssociationInfo.getId());
+ mAppToken.unlinkToDeath(this, 0);
mInputController.close();
}
@@ -257,7 +265,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
"Virtual device already have a virtual display with ID " + displayId);
}
mVirtualDisplayIds.add(displayId);
- return mGenericWindowPolicyController;
+ final GenericWindowPolicyController dwpc =
+ new GenericWindowPolicyController(FLAG_SECURE,
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ mWindowPolicyControllers.put(displayId, dwpc);
+ return dwpc;
}
void onVirtualDisplayRemovedLocked(int displayId) {
@@ -266,12 +278,27 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
"Virtual device doesn't have a virtual display with ID " + displayId);
}
mVirtualDisplayIds.remove(displayId);
+ mWindowPolicyControllers.remove(displayId);
}
int getOwnerUid() {
return mOwnerUid;
}
+ /**
+ * Returns true if an app with the given {@code uid} is currently running on this virtual
+ * device.
+ */
+ boolean isAppRunningOnVirtualDevice(int uid) {
+ final int size = mWindowPolicyControllers.size();
+ for (int i = 0; i < size; i++) {
+ if (mWindowPolicyControllers.valueAt(i).containsUid(uid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
interface OnDeviceCloseListener {
void onClose(int associationId);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 46e75f75ea9f..0db670e46909 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -252,7 +252,14 @@ public class VirtualDeviceManagerService extends SystemService {
@Override
public boolean isAppRunningOnAnyVirtualDevice(int uid) {
- // TODO(yukl): Implement this using DWPC.onRunningAppsChanged
+ synchronized (mVirtualDeviceManagerLock) {
+ int size = mVirtualDevices.size();
+ for (int i = 0; i < size; i++) {
+ if (mVirtualDevices.valueAt(i).isAppRunningOnVirtualDevice(uid)) {
+ return true;
+ }
+ }
+ }
return false;
}
}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 34c21f245d4f..f944d4fd8478 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -89,13 +89,13 @@ public class GestureLauncherService extends SystemService {
/**
* Default value of the power button "cooldown" period after the Emergency gesture is triggered.
- * See {@link Settings.Secure#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS}
+ * See {@link Settings.Global#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS}
*/
private static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT = 3000;
/**
* Maximum value of the power button "cooldown" period after the Emergency gesture is triggered.
- * The value read from {@link Settings.Secure#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS}
+ * The value read from {@link Settings.Global#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS}
* is capped at this maximum.
*/
@VisibleForTesting
@@ -284,8 +284,8 @@ public class GestureLauncherService extends SystemService {
Settings.Secure.getUriFor(Settings.Secure.EMERGENCY_GESTURE_ENABLED),
false, mSettingObserver, mUserId);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(
- Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS),
+ Settings.Global.getUriFor(
+ Settings.Global.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS),
false, mSettingObserver, mUserId);
}
@@ -469,9 +469,10 @@ public class GestureLauncherService extends SystemService {
*/
@VisibleForTesting
static int getEmergencyGesturePowerButtonCooldownPeriodMs(Context context, int userId) {
- int cooldown = Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
- EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT, userId);
+ int cooldown = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
+ EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT);
+
return Math.min(cooldown, EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX);
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 91cd2f6ad676..811f2f5e5283 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -91,6 +91,7 @@ import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.ICarrierPrivilegesListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -106,6 +107,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -149,6 +151,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
IPhoneStateListener callback;
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback;
+ ICarrierPrivilegesListener carrierPrivilegesListener;
int callerUid;
int callerPid;
@@ -173,6 +176,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
return (onOpportunisticSubscriptionsChangedListenerCallback != null);
}
+ boolean matchCarrierPrivilegesListener() {
+ return carrierPrivilegesListener != null;
+ }
+
boolean canReadCallLog() {
try {
return TelephonyPermissions.checkReadCallLog(
@@ -189,8 +196,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ " onSubscriptionsChangedListenererCallback="
+ onSubscriptionsChangedListenerCallback
+ " onOpportunisticSubscriptionsChangedListenererCallback="
- + onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId
- + " phoneId=" + phoneId + " events=" + eventList + "}";
+ + onOpportunisticSubscriptionsChangedListenerCallback
+ + " carrierPrivilegesListener=" + carrierPrivilegesListener
+ + " subId=" + subId + " phoneId=" + phoneId + " events=" + eventList + "}";
}
}
@@ -366,7 +374,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private List<BarringInfo> mBarringInfo = null;
- private boolean mCarrierNetworkChangeState = false;
+ private boolean[] mCarrierNetworkChangeState = null;
private PhoneCapability mPhoneCapability = null;
@@ -402,6 +410,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
*/
private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>>
mPreciseDataConnectionStates;
+
+ /** Per-phoneId snapshot of privileged packages (names + UIDs). */
+ private List<Pair<List<String>, int[]>> mCarrierPrivilegeStates;
+
/**
* Support backward compatibility for {@link android.telephony.TelephonyDisplayInfo}.
*/
@@ -675,6 +687,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones);
mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones);
mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones);
+ mCarrierNetworkChangeState = copyOf(mCarrierNetworkChangeState, mNumPhones);
mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones);
mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones);
mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones);
@@ -688,6 +701,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
cutListToSize(mBarringInfo, mNumPhones);
cutListToSize(mPhysicalChannelConfigs, mNumPhones);
cutListToSize(mLinkCapacityEstimateLists, mNumPhones);
+ cutListToSize(mCarrierPrivilegeStates, mNumPhones);
return;
}
@@ -720,6 +734,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mPreciseDataConnectionStates.add(new ArrayMap<>());
mBarringInfo.add(i, new BarringInfo());
+ mCarrierNetworkChangeState[i] = false;
mTelephonyDisplayInfos[i] = null;
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
@@ -727,6 +742,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mAllowedNetworkTypeReason[i] = -1;
mAllowedNetworkTypeValue[i] = -1;
mLinkCapacityEstimateLists.add(i, INVALID_LCE_LIST);
+ mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0]));
}
}
@@ -784,6 +800,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones];
mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones];
mBarringInfo = new ArrayList<>();
+ mCarrierNetworkChangeState = new boolean[numPhones];
mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones];
mPhysicalChannelConfigs = new ArrayList<>();
mAllowedNetworkTypeReason = new int[numPhones];
@@ -791,6 +808,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mIsDataEnabled = new boolean[numPhones];
mDataEnabledReason = new int[numPhones];
mLinkCapacityEstimateLists = new ArrayList<>();
+ mCarrierPrivilegeStates = new ArrayList<>();
for (int i = 0; i < numPhones; i++) {
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
@@ -820,6 +838,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mPreciseDataConnectionStates.add(new ArrayMap<>());
mBarringInfo.add(i, new BarringInfo());
+ mCarrierNetworkChangeState[i] = false;
mTelephonyDisplayInfos[i] = null;
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
@@ -827,6 +846,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mAllowedNetworkTypeReason[i] = -1;
mAllowedNetworkTypeValue[i] = -1;
mLinkCapacityEstimateLists.add(i, INVALID_LCE_LIST);
+ mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0]));
}
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -1230,7 +1250,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
if (events.contains(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)) {
try {
- r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
+ r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1724,23 +1744,37 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
throw new SecurityException("notifyCarrierNetworkChange without carrier privilege");
}
+ for (int subId : subIds) {
+ notifyCarrierNetworkChangeWithPermission(subId, active);
+ }
+ }
+
+ @Override
+ public void notifyCarrierNetworkChangeWithSubId(int subId, boolean active) {
+ if (!TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext, subId)) {
+ throw new SecurityException(
+ "notifyCarrierNetworkChange without carrier privilege on subId " + subId);
+ }
+
+ notifyCarrierNetworkChangeWithPermission(subId, active);
+ }
+
+ private void notifyCarrierNetworkChangeWithPermission(int subId, boolean active) {
synchronized (mRecords) {
- mCarrierNetworkChangeState = active;
- for (int subId : subIds) {
- int phoneId = getPhoneIdFromSubId(subId);
+ int phoneId = getPhoneIdFromSubId(subId);
+ mCarrierNetworkChangeState[phoneId] = active;
- if (VDBG) {
- log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
- }
- for (Record r : mRecords) {
- if (r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
- && idMatch(r, subId, phoneId)) {
- try {
- r.callback.onCarrierNetworkChange(active);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
- }
+ if (VDBG) {
+ log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
+ }
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ r.callback.onCarrierNetworkChange(active);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
}
}
}
@@ -2748,6 +2782,104 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
@Override
+ public void addCarrierPrivilegesListener(
+ int phoneId,
+ ICarrierPrivilegesListener callback,
+ String callingPackage,
+ String callingFeatureId) {
+ int callerUserId = UserHandle.getCallingUserId();
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "addCarrierPrivilegesListener");
+ if (VDBG) {
+ log(
+ "listen carrier privs: E pkg=" + pii(callingPackage) + " phoneId=" + phoneId
+ + " uid=" + Binder.getCallingUid()
+ + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId
+ + " callback=" + callback
+ + " callback.asBinder=" + callback.asBinder());
+ }
+ if (!validatePhoneId(phoneId)) {
+ throw new IllegalArgumentException("Invalid slot index: " + phoneId);
+ }
+
+ synchronized (mRecords) {
+ Record r = add(
+ callback.asBinder(), Binder.getCallingUid(), Binder.getCallingPid(), false);
+
+ if (r == null) return;
+
+ r.context = mContext;
+ r.carrierPrivilegesListener = callback;
+ r.callingPackage = callingPackage;
+ r.callingFeatureId = callingFeatureId;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
+ r.phoneId = phoneId;
+ r.eventList = new ArraySet<>();
+ if (DBG) {
+ log("listen carrier privs: Register r=" + r);
+ }
+
+ Pair<List<String>, int[]> state = mCarrierPrivilegeStates.get(phoneId);
+ try {
+ r.carrierPrivilegesListener.onCarrierPrivilegesChanged(
+ Collections.unmodifiableList(state.first),
+ Arrays.copyOf(state.second, state.second.length));
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ }
+
+ @Override
+ public void removeCarrierPrivilegesListener(
+ ICarrierPrivilegesListener callback, String callingPackage) {
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "removeCarrierPrivilegesListener");
+ remove(callback.asBinder());
+ }
+
+ @Override
+ public void notifyCarrierPrivilegesChanged(
+ int phoneId, List<String> privilegedPackageNames, int[] privilegedUids) {
+ if (!checkNotifyPermission("notifyCarrierPrivilegesChanged")) {
+ return;
+ }
+ if (!validatePhoneId(phoneId)) return;
+ if (VDBG) {
+ log(
+ "notifyCarrierPrivilegesChanged: phoneId=" + phoneId
+ + ", <packages=" + pii(privilegedPackageNames)
+ + ", uids=" + Arrays.toString(privilegedUids) + ">");
+ }
+ synchronized (mRecords) {
+ mCarrierPrivilegeStates.set(
+ phoneId, new Pair<>(privilegedPackageNames, privilegedUids));
+ for (Record r : mRecords) {
+ // Listeners are per-slot, not per-subscription. This is to provide a stable
+ // view across SIM profile switches.
+ if (!r.matchCarrierPrivilegesListener()
+ || !idMatch(r, SubscriptionManager.INVALID_SUBSCRIPTION_ID, phoneId)) {
+ continue;
+ }
+ try {
+ // Make sure even in-process listeners can't modify the values.
+ r.carrierPrivilegesListener.onCarrierPrivilegesChanged(
+ Collections.unmodifiableList(privilegedPackageNames),
+ Arrays.copyOf(privilegedUids, privilegedUids.length));
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -2788,6 +2920,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println("mOutgoingCallEmergencyNumber=" + mOutgoingCallEmergencyNumber[i]);
pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]);
pw.println("mBarringInfo=" + mBarringInfo.get(i));
+ pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState[i]);
pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
pw.println("mIsDataEnabled=" + mIsDataEnabled);
pw.println("mDataEnabledReason=" + mDataEnabledReason);
@@ -2795,9 +2928,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]);
pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs.get(i));
pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i));
+ // We need to obfuscate package names, and primitive arrays' native toString is ugly
+ Pair<List<String>, int[]> carrierPrivilegeState = mCarrierPrivilegeStates.get(i);
+ pw.println(
+ "mCarrierPrivilegeState=<packages=" + pii(carrierPrivilegeState.first)
+ + ", uids=" + Arrays.toString(carrierPrivilegeState.second) + ">");
pw.decreaseIndent();
}
- pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
+
pw.println("mPhoneCapability=" + mPhoneCapability);
pw.println("mActiveDataSubId=" + mActiveDataSubId);
pw.println("mRadioPowerState=" + mRadioPowerState);
@@ -3521,4 +3659,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private static String pii(String packageName) {
return Build.IS_DEBUGGABLE ? packageName : "***";
}
+
+ /** Redacts an entire list of package names if necessary. */
+ private static String pii(List<String> packageNames) {
+ if (packageNames.isEmpty() || Build.IS_DEBUGGABLE) return packageNames.toString();
+ return "[***, size=" + packageNames.size() + "]";
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ea345a7901a4..f0f6bd172770 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5915,6 +5915,10 @@ public class ActivityManagerService extends IActivityManager.Stub
if (targetPkg == null) {
throw new IllegalArgumentException("null target");
}
+ final int callingUserId = UserHandle.getUserId(r.uid);
+ if (mPackageManagerInt.filterAppAccess(targetPkg, r.uid, callingUserId)) {
+ return;
+ }
Preconditions.checkFlagsArgument(modeFlags, Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
@@ -5926,7 +5930,7 @@ public class ActivityManagerService extends IActivityManager.Stub
intent.setFlags(modeFlags);
final NeededUriGrants needed = mUgmInternal.checkGrantUriPermissionFromIntent(intent,
- r.uid, targetPkg, UserHandle.getUserId(r.uid));
+ r.uid, targetPkg, callingUserId);
mUgmInternal.grantUriPermissionUncheckedFromIntent(needed, null);
}
}
@@ -8042,7 +8046,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Obtain Incremental information if available
if (r != null && r.info != null && r.info.packageName != null) {
IncrementalStatesInfo incrementalStatesInfo =
- mPackageManagerInt.getIncrementalStatesInfo(r.info.packageName, r.uid,
+ mPackageManagerInt.getIncrementalStatesInfo(r.info.packageName, SYSTEM_UID,
r.userId);
if (incrementalStatesInfo != null) {
loadingProgress = incrementalStatesInfo.getProgress();
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 9180ef80ae71..336572f44be3 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -130,6 +130,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
@GuardedBy("this")
private Future<?> mBatteryLevelSync;
+ @GuardedBy("this")
+ private Future<?> mProcessStateSync;
+
// If both mStats and mWorkerLock need to be synchronized, mWorkerLock must be acquired first.
private final Object mWorkerLock = new Object();
@@ -316,6 +319,25 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
@Override
+ public Future<?> scheduleSyncDueToProcessStateChange(long delayMillis) {
+ synchronized (BatteryExternalStatsWorker.this) {
+ mProcessStateSync = scheduleDelayedSyncLocked(mProcessStateSync,
+ () -> scheduleSync("procstate-change", UPDATE_ON_PROC_STATE_CHANGE),
+ delayMillis);
+ return mProcessStateSync;
+ }
+ }
+
+ public void cancelSyncDueToProcessStateChange() {
+ synchronized (BatteryExternalStatsWorker.this) {
+ if (mProcessStateSync != null) {
+ mProcessStateSync.cancel(false);
+ mProcessStateSync = null;
+ }
+ }
+ }
+
+ @Override
public Future<?> scheduleCleanupDueToRemovedUser(int userId) {
synchronized (BatteryExternalStatsWorker.this) {
return mExecutorService.schedule(() -> {
@@ -434,6 +456,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
if ((updateFlags & UPDATE_CPU) != 0) {
cancelCpuSyncDueToWakelockChange();
}
+ if ((updateFlags & UPDATE_ON_PROC_STATE_CHANGE) == UPDATE_ON_PROC_STATE_CHANGE) {
+ cancelSyncDueToProcessStateChange();
+ }
}
try {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 5a543323bee3..af4ff58596a1 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2782,29 +2782,18 @@ public class OomAdjuster {
void setAttachingSchedGroupLSP(ProcessRecord app) {
int initialSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
final ProcessStateRecord state = app.mState;
- // If the process has been marked as foreground via Zygote.START_FLAG_USE_TOP_APP_PRIORITY,
- // then verify that the top priority is actually is applied.
+ // If the process has been marked as foreground, it is starting as the top app (with
+ // Zygote#START_AS_TOP_APP_ARG), so boost the thread priority of its default UI thread.
if (state.hasForegroundActivities()) {
- String fallbackReason = null;
try {
// The priority must be the same as how does {@link #applyOomAdjLSP} set for
// {@link ProcessList.SCHED_GROUP_TOP_APP}. We don't check render thread because it
// is not ready when attaching.
- if (Process.getProcessGroup(app.getPid()) == THREAD_GROUP_TOP_APP) {
- app.getWindowProcessController().onTopProcChanged();
- setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
- } else {
- fallbackReason = "not expected top priority";
- }
- } catch (Exception e) {
- fallbackReason = e.toString();
- }
- if (fallbackReason == null) {
+ app.getWindowProcessController().onTopProcChanged();
+ setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
initialSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
- } else {
- // The real scheduling group will depend on if there is any component of the process
- // did something during attaching.
- Slog.w(TAG, "Fallback pre-set sched group to default: " + fallbackReason);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to pre-set top priority to " + app + " " + e);
}
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 18ad1f557ee6..7fe270084e25 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static android.os.Process.SYSTEM_UID;
+
import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
import static com.android.server.am.ActivityManagerService.MY_PID;
@@ -440,7 +442,7 @@ class ProcessErrorStateRecord {
if (mApp.info != null && mApp.info.packageName != null && packageManagerInternal != null) {
IncrementalStatesInfo incrementalStatesInfo =
packageManagerInternal.getIncrementalStatesInfo(
- mApp.info.packageName, mApp.uid, mApp.userId);
+ mApp.info.packageName, SYSTEM_UID, mApp.userId);
if (incrementalStatesInfo != null) {
loadingProgress = incrementalStatesInfo.getProgress();
}
diff --git a/services/core/java/com/android/server/app/GameClassifier.java b/services/core/java/com/android/server/app/GameClassifier.java
new file mode 100644
index 000000000000..e20bf46e3b42
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameClassifier.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import android.annotation.NonNull;
+import android.os.UserHandle;
+
+/**
+ * Responsible for determining if a given application is a game.
+ */
+interface GameClassifier {
+
+ /**
+ * Returns {@code true} if the application associated with the given {@code packageName} is
+ * considered to be a game. The application is queried as the user associated with the given
+ * {@code userHandle}.
+ */
+ boolean isGame(@NonNull String packageName, @NonNull UserHandle userHandle);
+}
diff --git a/services/core/java/com/android/server/app/GameClassifierImpl.java b/services/core/java/com/android/server/app/GameClassifierImpl.java
new file mode 100644
index 000000000000..8f5b0f0a5f42
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameClassifierImpl.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import android.annotation.NonNull;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+
+final class GameClassifierImpl implements GameClassifier {
+
+ private final PackageManager mPackageManager;
+
+ GameClassifierImpl(@NonNull PackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
+ @Override
+ public boolean isGame(@NonNull String packageName, @NonNull UserHandle userHandle) {
+ @ApplicationInfo.Category
+ int applicationCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+
+ try {
+ applicationCategory =
+ mPackageManager.getApplicationInfoAsUser(
+ packageName,
+ 0,
+ userHandle.getIdentifier()).category;
+ } catch (PackageManager.NameNotFoundException ex) {
+ return false;
+ }
+
+ return applicationCategory == ApplicationInfo.CATEGORY_GAME;
+ }
+}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index fc48cd5507a8..0980f4077489 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -76,6 +76,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.CompatibilityOverrideConfig;
import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
@@ -563,7 +564,12 @@ public final class GameManagerService extends IGameManagerService.Stub {
mService.registerPackageReceiver();
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) {
- mGameServiceController = new GameServiceController(context);
+ mGameServiceController = new GameServiceController(
+ BackgroundThread.getExecutor(),
+ new GameServiceProviderSelectorImpl(
+ getContext().getResources(),
+ getContext().getPackageManager()),
+ new GameServiceProviderInstanceFactoryImpl(getContext()));
}
}
@@ -586,6 +592,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
@Override
+ public void onUserUnlocking(@NonNull TargetUser user) {
+ super.onUserUnlocking(user);
+ if (mGameServiceController != null) {
+ mGameServiceController.notifyUserUnlocking(user);
+ }
+ }
+
+ @Override
public void onUserStopping(@NonNull TargetUser user) {
mService.onUserStopping(user.getUserIdentifier());
if (mGameServiceController != null) {
diff --git a/services/core/java/com/android/server/app/GameServiceConnection.java b/services/core/java/com/android/server/app/GameServiceConnection.java
deleted file mode 100644
index b60778915de7..000000000000
--- a/services/core/java/com/android/server/app/GameServiceConnection.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.app;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.service.games.GameService;
-import android.service.games.IGameService;
-import android.util.Slog;
-
-final class GameServiceConnection {
- private static final String TAG = "GameServiceConnection";
- private static final boolean DEBUG = false;
-
- private final Context mContext;
- private final ComponentName mGameServiceComponent;
- private final int mUser;
- private boolean mIsBound;
- @Nullable
- private IGameService mGameService;
- private final ServiceConnection mConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) {
- Slog.d(TAG, "onServiceConnected to " + name + " for user(" + mUser + ")");
- }
-
- mGameService = IGameService.Stub.asInterface(service);
- try {
- mGameService.connected();
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException while calling ready", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) {
- Slog.d(TAG, "onServiceDisconnected to " + name);
- }
-
- mGameService = null;
- }
- };
-
- GameServiceConnection(Context context, ComponentName gameServiceComponent, int user) {
- mContext = context;
- mGameServiceComponent = gameServiceComponent;
- mUser = user;
- }
-
- public void connect() {
- if (mIsBound) {
- Slog.v(TAG, "Already bound, ignoring start.");
- return;
- }
-
- Intent intent = new Intent(GameService.SERVICE_INTERFACE);
- intent.setComponent(mGameServiceComponent);
- mIsBound = mContext.bindServiceAsUser(intent, mConnection,
- Context.BIND_AUTO_CREATE
- | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser));
- if (!mIsBound) {
- Slog.w(TAG, "Failed binding to game service " + mGameServiceComponent);
- }
- }
-
- public void disconnect() {
- try {
- if (mGameService != null) {
- mGameService.disconnected();
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in shutdown", e);
- }
-
- if (mIsBound) {
- mContext.unbindService(mConnection);
- mIsBound = false;
- }
- }
-}
diff --git a/services/core/java/com/android/server/app/GameServiceController.java b/services/core/java/com/android/server/app/GameServiceController.java
index d056ea9f359a..ac720b9c2971 100644
--- a/services/core/java/com/android/server/app/GameServiceController.java
+++ b/services/core/java/com/android/server/app/GameServiceController.java
@@ -18,40 +18,56 @@ package com.android.server.app;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.service.games.GameService;
-import android.text.TextUtils;
+import android.annotation.WorkerThread;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
-import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+/**
+ * Responsible for managing the Game Service API.
+ *
+ * Key responsibilities selecting the active Game Service provider, binding to the Game Service
+ * provider services, and driving the GameService/GameSession lifecycles.
+ */
final class GameServiceController {
private static final String TAG = "GameServiceController";
- private static final boolean DEBUG = false;
- private final Context mContext;
- @Nullable
- private SystemService.TargetUser mCurrentForegroundUser;
- private boolean mHasBootCompleted;
- @Nullable
- private GameServiceConnection mGameServiceConnection;
+ private final Object mLock = new Object();
+ private final Executor mBackgroundExecutor;
+ private final GameServiceProviderSelector mGameServiceProviderSelector;
+ private final GameServiceProviderInstanceFactory mGameServiceProviderInstanceFactory;
- GameServiceController(Context context) {
- mContext = context;
+ private volatile boolean mHasBootCompleted;
+ @Nullable
+ private volatile SystemService.TargetUser mCurrentForegroundUser;
+ @GuardedBy("mLock")
+ @Nullable
+ private volatile GameServiceProviderConfiguration mActiveGameServiceProviderConfiguration;
+ @GuardedBy("mLock")
+ @Nullable
+ private volatile GameServiceProviderInstance mGameServiceProviderInstance;
+
+ GameServiceController(
+ @NonNull Executor backgroundExecutor,
+ @NonNull GameServiceProviderSelector gameServiceProviderSelector,
+ @NonNull GameServiceProviderInstanceFactory gameServiceProviderInstanceFactory) {
+ mGameServiceProviderInstanceFactory = gameServiceProviderInstanceFactory;
+ mBackgroundExecutor = backgroundExecutor;
+ mGameServiceProviderSelector = gameServiceProviderSelector;
}
void onBootComplete() {
+ if (mHasBootCompleted) {
+ return;
+ }
mHasBootCompleted = true;
- evaluateGameServiceConnection();
+ mBackgroundExecutor.execute(this::evaluateActiveGameServiceProvider);
}
void notifyUserStarted(@NonNull SystemService.TargetUser user) {
@@ -59,96 +75,86 @@ final class GameServiceController {
return;
}
- mCurrentForegroundUser = user;
- evaluateGameServiceConnection();
+ setCurrentForegroundUserAndEvaluateProvider(user);
}
void notifyNewForegroundUser(@NonNull SystemService.TargetUser user) {
- mCurrentForegroundUser = user;
- evaluateGameServiceConnection();
+ setCurrentForegroundUserAndEvaluateProvider(user);
}
- void notifyUserStopped(@NonNull SystemService.TargetUser user) {
- if (mCurrentForegroundUser == null
- || mCurrentForegroundUser.getUserIdentifier() != user.getUserIdentifier()) {
+ void notifyUserUnlocking(@NonNull SystemService.TargetUser user) {
+ boolean isSameAsForegroundUser =
+ mCurrentForegroundUser != null
+ && mCurrentForegroundUser.getUserIdentifier() == user.getUserIdentifier();
+ if (!isSameAsForegroundUser) {
return;
}
- mCurrentForegroundUser = null;
- evaluateGameServiceConnection();
+ // It is likely that the Game Service provider's components are not Direct Boot mode aware
+ // and will not be capable of running until the user has unlocked the device. To allow for
+ // this we re-evaluate the active game service provider once these components are available.
+
+ mBackgroundExecutor.execute(this::evaluateActiveGameServiceProvider);
}
- private void evaluateGameServiceConnection() {
- if (!mHasBootCompleted) {
+ void notifyUserStopped(@NonNull SystemService.TargetUser user) {
+ boolean isSameAsForegroundUser =
+ mCurrentForegroundUser != null
+ && mCurrentForegroundUser.getUserIdentifier() == user.getUserIdentifier();
+ if (!isSameAsForegroundUser) {
return;
}
- // TODO(b/204565942): Only shutdown the existing service connection if the game service
- // provider or user has changed.
- if (mGameServiceConnection != null) {
- mGameServiceConnection.disconnect();
- mGameServiceConnection = null;
- }
+ setCurrentForegroundUserAndEvaluateProvider(null);
+ }
- boolean isUserSupported =
- mCurrentForegroundUser != null
- && mCurrentForegroundUser.isFull()
- && !mCurrentForegroundUser.isManagedProfile();
- if (!isUserSupported) {
- if (DEBUG && mCurrentForegroundUser != null) {
- Slog.d(TAG, "User not supported: " + mCurrentForegroundUser);
- }
+ private void setCurrentForegroundUserAndEvaluateProvider(
+ @Nullable SystemService.TargetUser user) {
+ boolean hasUserChanged =
+ !Objects.equals(mCurrentForegroundUser, user);
+ if (!hasUserChanged) {
return;
}
+ mCurrentForegroundUser = user;
+
+ mBackgroundExecutor.execute(this::evaluateActiveGameServiceProvider);
+ }
- ComponentName gameServiceComponentName =
- determineGameServiceComponentName(mCurrentForegroundUser.getUserIdentifier());
- if (gameServiceComponentName == null) {
+ @WorkerThread
+ private void evaluateActiveGameServiceProvider() {
+ if (!mHasBootCompleted) {
return;
}
- mGameServiceConnection = new GameServiceConnection(
- mContext,
- gameServiceComponentName,
- mCurrentForegroundUser.getUserIdentifier());
- mGameServiceConnection.connect();
- }
+ synchronized (mLock) {
+ GameServiceProviderConfiguration selectedGameServiceProviderConfiguration =
+ mGameServiceProviderSelector.get(mCurrentForegroundUser);
- @Nullable
- private ComponentName determineGameServiceComponentName(int userId) {
- String gameServicePackage =
- mContext.getResources().getString(
- com.android.internal.R.string.config_systemGameService);
- if (TextUtils.isEmpty(gameServicePackage)) {
- if (DEBUG) {
- Slog.d(TAG, "No game service package defined");
+ boolean didActiveGameServiceProviderChanged =
+ !Objects.equals(selectedGameServiceProviderConfiguration,
+ mActiveGameServiceProviderConfiguration);
+ if (!didActiveGameServiceProviderChanged) {
+ return;
}
- return null;
- }
- List<ResolveInfo> gameServiceResolveInfos =
- mContext.getPackageManager().queryIntentServicesAsUser(
- new Intent(GameService.SERVICE_INTERFACE).setPackage(gameServicePackage),
- PackageManager.MATCH_SYSTEM_ONLY,
- userId);
+ if (mGameServiceProviderInstance != null) {
+ Slog.i(TAG, "Stopping Game Service provider: "
+ + mActiveGameServiceProviderConfiguration);
+ mGameServiceProviderInstance.stop();
+ }
- if (gameServiceResolveInfos.isEmpty()) {
- Slog.v(TAG, "No available game service found for user id: " + userId);
- return null;
- }
+ mActiveGameServiceProviderConfiguration = selectedGameServiceProviderConfiguration;
- for (ResolveInfo resolveInfo : gameServiceResolveInfos) {
- if (resolveInfo.serviceInfo == null) {
- continue;
- }
- final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
- if (!serviceInfo.isEnabled()) {
- continue;
+ if (mActiveGameServiceProviderConfiguration == null) {
+ return;
}
- return serviceInfo.getComponentName();
- }
- Slog.v(TAG, "No game service found for user id: " + userId);
- return null;
+ Slog.i(TAG,
+ "Starting Game Service provider: " + mActiveGameServiceProviderConfiguration);
+ mGameServiceProviderInstance =
+ mGameServiceProviderInstanceFactory.create(
+ mActiveGameServiceProviderConfiguration);
+ mGameServiceProviderInstance.start();
+ }
}
}
diff --git a/services/core/java/com/android/server/app/GameServiceProviderConfiguration.java b/services/core/java/com/android/server/app/GameServiceProviderConfiguration.java
new file mode 100644
index 000000000000..7c8f251f35fe
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameServiceProviderConfiguration.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.os.UserHandle;
+
+import java.util.Objects;
+
+/**
+ * Representation of a {@link android.service.games.GameService} provider configuration.
+ */
+final class GameServiceProviderConfiguration {
+ private final UserHandle mUserHandle;
+ private final ComponentName mGameServiceComponentName;
+ private final ComponentName mGameSessionServiceComponentName;
+
+ GameServiceProviderConfiguration(
+ @NonNull UserHandle userHandle,
+ @NonNull ComponentName gameServiceComponentName,
+ @NonNull ComponentName gameSessionServiceComponentName) {
+ Objects.requireNonNull(userHandle);
+ Objects.requireNonNull(gameServiceComponentName);
+ Objects.requireNonNull(gameSessionServiceComponentName);
+
+ this.mUserHandle = userHandle;
+ this.mGameServiceComponentName = gameServiceComponentName;
+ this.mGameSessionServiceComponentName = gameSessionServiceComponentName;
+ }
+
+ @NonNull
+ public UserHandle getUserHandle() {
+ return mUserHandle;
+ }
+
+ @NonNull
+ public ComponentName getGameServiceComponentName() {
+ return mGameServiceComponentName;
+ }
+
+ @NonNull
+ public ComponentName getGameSessionServiceComponentName() {
+ return mGameSessionServiceComponentName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof GameServiceProviderConfiguration)) {
+ return false;
+ }
+
+ GameServiceProviderConfiguration that = (GameServiceProviderConfiguration) o;
+ return mUserHandle.equals(that.mUserHandle)
+ && mGameServiceComponentName.equals(that.mGameServiceComponentName)
+ && mGameSessionServiceComponentName.equals(that.mGameSessionServiceComponentName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUserHandle, mGameServiceComponentName,
+ mGameSessionServiceComponentName);
+ }
+
+ @Override
+ public String toString() {
+ return "GameServiceProviderConfiguration{"
+ + "mUserHandle="
+ + mUserHandle
+ + ", gameServiceComponentName="
+ + mGameServiceComponentName
+ + ", gameSessionServiceComponentName="
+ + mGameSessionServiceComponentName
+ + '}';
+ }
+}
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstance.java b/services/core/java/com/android/server/app/GameServiceProviderInstance.java
new file mode 100644
index 000000000000..e83f9acca66e
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstance.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+/**
+ * Representation of an instance of a Game Service provider.
+ *
+ * This includes maintaining the bindings and driving the interactions with the provider's
+ * implementations of {@link android.service.games.GameService} and
+ * {@link android.service.games.GameSessionService}.
+ */
+interface GameServiceProviderInstance {
+ /**
+ * Begins running the Game Service provider instance.
+ */
+ void start();
+
+ /**
+ * Stops running the Game Service provider instance.
+ */
+ void stop();
+}
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactory.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactory.java
new file mode 100644
index 000000000000..7640cc555446
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import android.annotation.NonNull;
+
+/**
+ * Factory for creating {@link GameServiceProviderInstance}.
+ */
+interface GameServiceProviderInstanceFactory {
+
+ @NonNull
+ GameServiceProviderInstance create(@NonNull
+ GameServiceProviderConfiguration gameServiceProviderConfiguration);
+}
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
new file mode 100644
index 000000000000..d5ac03ab7c0d
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import android.annotation.NonNull;
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.content.Intent;
+import android.service.games.GameService;
+import android.service.games.GameSessionService;
+import android.service.games.IGameService;
+import android.service.games.IGameSessionService;
+
+import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.BackgroundThread;
+
+final class GameServiceProviderInstanceFactoryImpl implements GameServiceProviderInstanceFactory {
+ private final Context mContext;
+
+ GameServiceProviderInstanceFactoryImpl(@NonNull Context context) {
+ this.mContext = context;
+ }
+
+ @NonNull
+ @Override
+ public GameServiceProviderInstance create(@NonNull
+ GameServiceProviderConfiguration gameServiceProviderConfiguration) {
+ return new GameServiceProviderInstanceImpl(
+ gameServiceProviderConfiguration.getUserHandle(),
+ BackgroundThread.getExecutor(),
+ new GameClassifierImpl(mContext.getPackageManager()),
+ ActivityTaskManager.getService(),
+ new GameServiceConnector(mContext, gameServiceProviderConfiguration),
+ new GameSessionServiceConnector(mContext, gameServiceProviderConfiguration));
+ }
+
+ private static final class GameServiceConnector extends ServiceConnector.Impl<IGameService> {
+ private static final int DISABLE_AUTOMATIC_DISCONNECT_TIMEOUT = 0;
+ private static final int BINDING_FLAGS = Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS;
+
+ GameServiceConnector(
+ @NonNull Context context,
+ @NonNull GameServiceProviderConfiguration configuration) {
+ super(context, new Intent(GameService.ACTION_GAME_SERVICE)
+ .setComponent(configuration.getGameServiceComponentName()),
+ BINDING_FLAGS, configuration.getUserHandle().getIdentifier(),
+ IGameService.Stub::asInterface);
+ }
+
+ @Override
+ protected long getAutoDisconnectTimeoutMs() {
+ return DISABLE_AUTOMATIC_DISCONNECT_TIMEOUT;
+ }
+ }
+
+ private static final class GameSessionServiceConnector extends
+ ServiceConnector.Impl<IGameSessionService> {
+ private static final int DISABLE_AUTOMATIC_DISCONNECT_TIMEOUT = 0;
+ private static final int BINDING_FLAGS =
+ Context.BIND_TREAT_LIKE_ACTIVITY
+ | Context.BIND_SCHEDULE_LIKE_TOP_APP
+ | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS;
+
+ GameSessionServiceConnector(
+ @NonNull Context context,
+ @NonNull GameServiceProviderConfiguration configuration) {
+ super(context, new Intent(GameSessionService.ACTION_GAME_SESSION_SERVICE)
+ .setComponent(configuration.getGameSessionServiceComponentName()),
+ BINDING_FLAGS, configuration.getUserHandle().getIdentifier(),
+ IGameSessionService.Stub::asInterface);
+ }
+
+ @Override
+ protected long getAutoDisconnectTimeoutMs() {
+ return DISABLE_AUTOMATIC_DISCONNECT_TIMEOUT;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
new file mode 100644
index 000000000000..3f3f257aedc5
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import android.annotation.NonNull;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
+import android.content.ComponentName;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.games.CreateGameSessionRequest;
+import android.service.games.IGameService;
+import android.service.games.IGameSession;
+import android.service.games.IGameSessionService;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.infra.ServiceConnector;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+final class GameServiceProviderInstanceImpl implements GameServiceProviderInstance {
+ private static final String TAG = "GameServiceProviderInstance";
+ private static final int CREATE_GAME_SESSION_TIMEOUT_MS = 10_000;
+ private static final boolean DEBUG = false;
+
+ private final TaskStackListener mTaskStackListener = new TaskStackListener() {
+ @Override
+ public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
+ if (componentName == null) {
+ return;
+ }
+
+ mBackgroundExecutor.execute(() -> {
+ GameServiceProviderInstanceImpl.this.onTaskCreated(taskId, componentName);
+ });
+ }
+
+ @Override
+ public void onTaskRemoved(int taskId) throws RemoteException {
+ mBackgroundExecutor.execute(() -> {
+ GameServiceProviderInstanceImpl.this.onTaskRemoved(taskId);
+ });
+ }
+ };
+ private final Object mLock = new Object();
+ private final UserHandle mUserHandle;
+ private final Executor mBackgroundExecutor;
+ private final GameClassifier mGameClassifier;
+ private final IActivityTaskManager mActivityTaskManager;
+ private final ServiceConnector<IGameService> mGameServiceConnector;
+ private final ServiceConnector<IGameSessionService> mGameSessionServiceConnector;
+
+ @GuardedBy("mLock")
+ private final ConcurrentHashMap<Integer, GameSessionRecord> mGameSessions =
+ new ConcurrentHashMap<>();
+ @GuardedBy("mLock")
+ private volatile boolean mIsRunning;
+
+ GameServiceProviderInstanceImpl(
+ UserHandle userHandle,
+ @NonNull Executor backgroundExecutor,
+ @NonNull GameClassifier gameClassifier,
+ @NonNull IActivityTaskManager activityTaskManager,
+ @NonNull ServiceConnector<IGameService> gameServiceConnector,
+ @NonNull ServiceConnector<IGameSessionService> gameSessionServiceConnector) {
+ mUserHandle = userHandle;
+ mBackgroundExecutor = backgroundExecutor;
+ mGameClassifier = gameClassifier;
+ mActivityTaskManager = activityTaskManager;
+ mGameServiceConnector = gameServiceConnector;
+ mGameSessionServiceConnector = gameSessionServiceConnector;
+ }
+
+ @Override
+ public void start() {
+ synchronized (mLock) {
+ startLocked();
+ }
+ }
+
+ @Override
+ public void stop() {
+ synchronized (mLock) {
+ stopLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void startLocked() {
+ if (mIsRunning) {
+ return;
+ }
+ mIsRunning = true;
+
+ // TODO(b/204503192): In cases where the connection to the game service fails retry with
+ // back off mechanism.
+ AndroidFuture<Void> unusedPostConnectedFuture = mGameServiceConnector.post(gameService -> {
+ gameService.connected();
+ });
+
+ try {
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to register task stack listener", e);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void stopLocked() {
+ if (!mIsRunning) {
+ return;
+ }
+ mIsRunning = false;
+
+ try {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to unregister task stack listener", e);
+ }
+
+ for (GameSessionRecord gameSessionRecord : mGameSessions.values()) {
+ IGameSession gameSession = gameSessionRecord.getGameSession();
+ if (gameSession == null) {
+ continue;
+ }
+
+ try {
+ gameSession.destroy();
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to destroy session: " + gameSessionRecord, ex);
+ }
+ }
+ mGameSessions.clear();
+
+ // TODO(b/204503192): It is possible that the game service is disconnected. In this
+ // case we should avoid rebinding just to shut it down again.
+ AndroidFuture<Void> unusedPostDisconnectedFuture =
+ mGameServiceConnector.post(gameService -> {
+ gameService.disconnected();
+ });
+ mGameServiceConnector.unbind();
+ mGameSessionServiceConnector.unbind();
+ }
+
+ private void onTaskCreated(int taskId, @NonNull ComponentName componentName) {
+ String packageName = componentName.getPackageName();
+ if (!mGameClassifier.isGame(packageName, mUserHandle)) {
+ return;
+ }
+
+ synchronized (mLock) {
+ createGameSessionLocked(taskId, componentName);
+ }
+ }
+
+ private void onTaskRemoved(int taskId) {
+ synchronized (mLock) {
+ boolean isTaskAssociatedWithGameSession = mGameSessions.containsKey(taskId);
+ if (!isTaskAssociatedWithGameSession) {
+ return;
+ }
+
+ destroyGameSessionLocked(taskId);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void createGameSessionLocked(int sessionId, @NonNull ComponentName componentName) {
+ if (DEBUG) {
+ Slog.i(TAG, "createGameSession() id: " + sessionId + " component: " + componentName);
+ }
+
+ if (!mIsRunning) {
+ return;
+ }
+
+ GameSessionRecord existingGameSessionRecord = mGameSessions.get(sessionId);
+ if (existingGameSessionRecord != null) {
+ Slog.w(TAG, "Existing game session found for task (id: " + sessionId
+ + ") creation. Ignoring.");
+ return;
+ }
+
+ GameSessionRecord gameSessionRecord = GameSessionRecord.pendingGameSession(sessionId,
+ componentName);
+ mGameSessions.put(sessionId, gameSessionRecord);
+
+ // TODO(b/207035150): Allow the game service provider to determine if a game session
+ // should be created. For now we will assume all games should have a session.
+ AndroidFuture<IBinder> gameSessionFuture = new AndroidFuture<IBinder>()
+ .orTimeout(CREATE_GAME_SESSION_TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ .whenCompleteAsync((gameSessionIBinder, exception) -> {
+ IGameSession gameSession = IGameSession.Stub.asInterface(gameSessionIBinder);
+ if (exception != null || gameSession == null) {
+ Slog.w(TAG, "Failed to create GameSession: " + gameSessionRecord,
+ exception);
+ synchronized (mLock) {
+ destroyGameSessionLocked(sessionId);
+ }
+ return;
+ }
+
+ synchronized (mLock) {
+ attachGameSessionLocked(sessionId, gameSession);
+ }
+ }, mBackgroundExecutor);
+
+ AndroidFuture<Void> unusedPostCreateGameSessionFuture =
+ mGameSessionServiceConnector.post(gameService -> {
+ CreateGameSessionRequest createGameSessionRequest =
+ new CreateGameSessionRequest(sessionId, componentName.getPackageName());
+ gameService.create(createGameSessionRequest, gameSessionFuture);
+ });
+ }
+
+ @GuardedBy("mLock")
+ private void attachGameSessionLocked(int sessionId, @NonNull IGameSession gameSession) {
+ if (DEBUG) {
+ Slog.i(TAG, "attachGameSession() id: " + sessionId);
+ }
+
+ GameSessionRecord gameSessionRecord = mGameSessions.get(sessionId);
+ if (gameSessionRecord == null) {
+ Slog.w(TAG, "No associated game session record. Destroying id: " + sessionId);
+
+ try {
+ gameSession.destroy();
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to destroy session: " + gameSessionRecord, ex);
+ }
+ return;
+ }
+
+ mGameSessions.put(sessionId, gameSessionRecord.withGameSession(gameSession));
+ }
+
+ @GuardedBy("mLock")
+ private void destroyGameSessionLocked(int sessionId) {
+ // TODO(b/204503192): Limit the lifespan of the game session in the Game Service provider
+ // to only when the associated task is running. Right now it is possible for a task to
+ // move into the background and for all associated processes to die and for the Game Session
+ // provider's GameSessionService to continue to be running. Ideally we could unbind the
+ // service when this happens.
+ if (DEBUG) {
+ Slog.i(TAG, "destroyGameSession() id: " + sessionId);
+ }
+
+ GameSessionRecord gameSessionRecord = mGameSessions.remove(sessionId);
+ if (gameSessionRecord == null) {
+ if (DEBUG) {
+ Slog.w(TAG, "No game session found for id: " + sessionId);
+ }
+ return;
+ }
+
+ IGameSession gameSession = gameSessionRecord.getGameSession();
+ if (gameSession != null) {
+ try {
+ gameSession.destroy();
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to destroy session: " + gameSessionRecord, ex);
+ }
+ }
+
+ if (mGameSessions.isEmpty()) {
+ if (DEBUG) {
+ Slog.i(TAG, "No active game sessions. Disconnecting GameSessionService");
+ }
+
+ if (mGameSessionServiceConnector != null) {
+ mGameSessionServiceConnector.unbind();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/app/GameServiceProviderSelector.java b/services/core/java/com/android/server/app/GameServiceProviderSelector.java
new file mode 100644
index 000000000000..51d35157b332
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameServiceProviderSelector.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import android.annotation.Nullable;
+
+import com.android.server.SystemService;
+
+/**
+ * Responsible for determining what the active Game Service provider should be.
+ */
+interface GameServiceProviderSelector {
+
+ /**
+ * Returns the {@link GameServiceProviderConfiguration} associated with the selected Game
+ * Service provider for the given user or {@code null} if none should be used.
+ */
+ @Nullable
+ GameServiceProviderConfiguration get(@Nullable SystemService.TargetUser user);
+}
diff --git a/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java b/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java
new file mode 100644
index 000000000000..54ef70715b86
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.UserHandle;
+import android.service.games.GameService;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.server.SystemService;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.List;
+
+final class GameServiceProviderSelectorImpl implements GameServiceProviderSelector {
+ private static final String TAG = "GameServiceProviderSelector";
+ private static final String GAME_SERVICE_NODE_NAME = "game-service";
+ private static final boolean DEBUG = false;
+
+ private final Resources mResources;
+ private final PackageManager mPackageManager;
+
+ GameServiceProviderSelectorImpl(@NonNull Resources resources,
+ @NonNull PackageManager packageManager) {
+ mResources = resources;
+ mPackageManager = packageManager;
+ }
+
+ @Override
+ @Nullable
+ public GameServiceProviderConfiguration get(@Nullable SystemService.TargetUser user) {
+ if (user == null) {
+ return null;
+ }
+
+ boolean isUserSupported = user.isFull() && !user.isManagedProfile();
+ if (!isUserSupported) {
+ Slog.i(TAG, "Game Service not supported for user: " + user.getUserIdentifier());
+ return null;
+ }
+
+ String gameServicePackage =
+ mResources.getString(
+ com.android.internal.R.string.config_systemGameService);
+
+ if (TextUtils.isEmpty(gameServicePackage)) {
+ Slog.w(TAG, "No game service package defined");
+ return null;
+ }
+
+ int userId = user.getUserIdentifier();
+ List<ResolveInfo> gameServiceResolveInfos =
+ mPackageManager.queryIntentServicesAsUser(
+ new Intent(GameService.ACTION_GAME_SERVICE).setPackage(gameServicePackage),
+ PackageManager.GET_META_DATA | PackageManager.MATCH_SYSTEM_ONLY,
+ userId);
+ if (DEBUG) {
+ Slog.i(TAG, "Querying package: " + gameServicePackage + " and user id: " + userId);
+ Slog.i(TAG, "Found resolve infos: " + gameServiceResolveInfos);
+ }
+
+ if (gameServiceResolveInfos == null || gameServiceResolveInfos.isEmpty()) {
+ Slog.w(TAG, "No available game service found for user id: " + userId);
+ return null;
+ }
+
+ GameServiceProviderConfiguration selectedProvider = null;
+ for (ResolveInfo resolveInfo : gameServiceResolveInfos) {
+ if (resolveInfo.serviceInfo == null) {
+ continue;
+ }
+ ServiceInfo gameServiceServiceInfo = resolveInfo.serviceInfo;
+
+ ComponentName gameSessionServiceComponentName =
+ determineGameSessionServiceFromGameService(gameServiceServiceInfo);
+ if (gameSessionServiceComponentName == null) {
+ continue;
+ }
+
+ selectedProvider =
+ new GameServiceProviderConfiguration(
+ new UserHandle(userId),
+ gameServiceServiceInfo.getComponentName(),
+ gameSessionServiceComponentName);
+ break;
+ }
+
+ if (selectedProvider == null) {
+ Slog.w(TAG, "No valid game service found for user id: " + userId);
+ return null;
+ }
+
+ return selectedProvider;
+ }
+
+ @Nullable
+ private ComponentName determineGameSessionServiceFromGameService(
+ @NonNull ServiceInfo gameServiceServiceInfo) {
+ String gameSessionService;
+ try (XmlResourceParser parser = gameServiceServiceInfo.loadXmlMetaData(mPackageManager,
+ GameService.SERVICE_META_DATA)) {
+ if (parser == null) {
+ Slog.w(TAG, "No " + GameService.SERVICE_META_DATA + " meta-data found for "
+ + gameServiceServiceInfo.getComponentName());
+ return null;
+ }
+
+ Resources resources = mPackageManager.getResourcesForApplication(
+ gameServiceServiceInfo.packageName);
+
+ AttributeSet attributeSet = Xml.asAttributeSet(parser);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ // Do nothing
+ }
+
+ boolean isStartingTagGameService = GAME_SERVICE_NODE_NAME.equals(parser.getName());
+ if (!isStartingTagGameService) {
+ Slog.w(TAG, "Meta-data does not start with " + GAME_SERVICE_NODE_NAME + " tag");
+ return null;
+ }
+
+ TypedArray array = resources.obtainAttributes(attributeSet,
+ com.android.internal.R.styleable.GameService);
+ gameSessionService = array.getString(
+ com.android.internal.R.styleable.GameService_gameSessionService);
+ array.recycle();
+ } catch (PackageManager.NameNotFoundException | XmlPullParserException | IOException ex) {
+ Slog.w("Error while parsing meta-data for " + gameServiceServiceInfo.getComponentName(),
+ ex);
+ return null;
+ }
+
+ if (TextUtils.isEmpty(gameSessionService)) {
+ Slog.w(TAG, "No gameSessionService specified");
+ return null;
+ }
+ ComponentName componentName =
+ new ComponentName(gameServiceServiceInfo.packageName, gameSessionService);
+
+ try {
+ mPackageManager.getServiceInfo(componentName, /* flags= */ 0);
+ } catch (PackageManager.NameNotFoundException ex) {
+ Slog.w(TAG, "GameSessionService does not exist: " + componentName);
+ return null;
+ }
+
+ return componentName;
+ }
+}
diff --git a/services/core/java/com/android/server/app/GameSessionRecord.java b/services/core/java/com/android/server/app/GameSessionRecord.java
new file mode 100644
index 000000000000..329e9e8144e0
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameSessionRecord.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.service.games.IGameSession;
+
+import java.util.Objects;
+
+final class GameSessionRecord {
+
+ private final int mTaskId;
+ private final ComponentName mRootComponentName;
+ @Nullable
+ private final IGameSession mIGameSession;
+
+ static GameSessionRecord pendingGameSession(int taskId, ComponentName rootComponentName) {
+ return new GameSessionRecord(taskId, rootComponentName, /* gameSession= */ null);
+ }
+
+ private GameSessionRecord(
+ int taskId,
+ @NonNull ComponentName rootComponentName,
+ @Nullable IGameSession gameSession) {
+ this.mTaskId = taskId;
+ this.mRootComponentName = rootComponentName;
+ this.mIGameSession = gameSession;
+ }
+
+ @NonNull
+ public GameSessionRecord withGameSession(@NonNull IGameSession gameSession) {
+ Objects.requireNonNull(gameSession);
+ return new GameSessionRecord(mTaskId, mRootComponentName, gameSession);
+ }
+
+ @Nullable
+ public IGameSession getGameSession() {
+ return mIGameSession;
+ }
+
+ @Override
+ public String toString() {
+ return "GameSessionRecord{"
+ + "mTaskId="
+ + mTaskId
+ + ", mRootComponentName="
+ + mRootComponentName
+ + ", mIGameSession="
+ + mIGameSession
+ + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof GameSessionRecord)) {
+ return false;
+ }
+
+ GameSessionRecord that = (GameSessionRecord) o;
+ return mTaskId == that.mTaskId && mRootComponentName.equals(that.mRootComponentName)
+ && Objects.equals(mIGameSession, that.mIGameSession);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTaskId, mRootComponentName, mIGameSession);
+ }
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 47bd47ec20c8..3c557d0cc35c 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1314,6 +1314,7 @@ public class AppOpsService extends IAppOpsService.Stub {
event.getAttributionFlags(), event.getAttributionChainId());
}
+ events = isRunning ? mInProgressEvents : mPausedInProgressEvents;
InProgressStartOpEvent newEvent = events.get(binders.get(i));
if (newEvent != null) {
newEvent.numUnfinishedStarts += numPreviousUnfinishedStarts - 1;
diff --git a/services/core/java/com/android/server/backup/AppSpecificLocalesBackupHelper.java b/services/core/java/com/android/server/backup/AppSpecificLocalesBackupHelper.java
new file mode 100644
index 000000000000..1726da2ff05d
--- /dev/null
+++ b/services/core/java/com/android/server/backup/AppSpecificLocalesBackupHelper.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.backup;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.backup.BlobBackupHelper;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.locales.LocaleManagerInternal;
+
+/**
+ * Helper for backing up app-specific locales.
+ * <p>
+ * This helper is used in {@link com.android.server.backup.SystemBackupAgent}
+ */
+public class AppSpecificLocalesBackupHelper extends BlobBackupHelper {
+ private static final String TAG = "AppLocalesBackupHelper"; // must be < 23 chars
+ private static final boolean DEBUG = false;
+
+ // Current version of the blob schema
+ private static final int BLOB_VERSION = 1;
+
+ // Key under which the payload blob is stored
+ private static final String KEY_APP_LOCALES = "app_locales";
+
+ private final @UserIdInt int mUserId;
+
+ private final @NonNull LocaleManagerInternal mLocaleManagerInternal;
+
+ public AppSpecificLocalesBackupHelper(int userId) {
+ super(BLOB_VERSION, KEY_APP_LOCALES);
+ mUserId = userId;
+ mLocaleManagerInternal = LocalServices.getService(LocaleManagerInternal.class);
+ }
+
+ @Override
+ protected byte[] getBackupPayload(String key) {
+ if (DEBUG) {
+ Slog.d(TAG, "Handling backup of " + key);
+ }
+
+ byte[] newPayload = null;
+ if (KEY_APP_LOCALES.equals(key)) {
+ try {
+ newPayload = mLocaleManagerInternal.getBackupPayload(mUserId);
+ } catch (Exception e) {
+ // Treat as no data
+ Slog.e(TAG, "Couldn't communicate with locale manager", e);
+ newPayload = null;
+ }
+ } else {
+ Slog.w(TAG, "Unexpected backup key " + key);
+ }
+ return newPayload;
+ }
+
+ @Override
+ protected void applyRestoredPayload(String key, byte[] payload) {
+ if (DEBUG) {
+ Slog.d(TAG, "Handling restore of " + key);
+ }
+
+ if (KEY_APP_LOCALES.equals(key)) {
+ try {
+ mLocaleManagerInternal.stageAndApplyRestoredPayload(payload, mUserId);
+ } catch (Exception e) {
+ Slog.e(TAG, "Couldn't communicate with locale manager", e);
+ }
+ } else {
+ Slog.w(TAG, "Unexpected restore key " + key);
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index fa1820456fb9..d39d2d1eefb7 100644
--- a/services/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -57,6 +57,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
private static final String ACCOUNT_MANAGER_HELPER = "account_manager";
private static final String SLICES_HELPER = "slices";
private static final String PEOPLE_HELPER = "people";
+ private static final String APP_LOCALES_HELPER = "app_locales";
// These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME
// are also used in the full-backup file format, so must not change unless steps are
@@ -83,7 +84,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY;
private static final Set<String> sEligibleForMultiUser = Sets.newArraySet(
- PERMISSION_HELPER, NOTIFICATION_HELPER, SYNC_SETTINGS_HELPER);
+ PERMISSION_HELPER, NOTIFICATION_HELPER, SYNC_SETTINGS_HELPER, APP_LOCALES_HELPER);
private int mUserId = UserHandle.USER_SYSTEM;
@@ -102,6 +103,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
addHelper(SLICES_HELPER, new SliceBackupHelper(this));
addHelper(PEOPLE_HELPER, new PeopleBackupHelper(mUserId));
+ addHelper(APP_LOCALES_HELPER, new AppSpecificLocalesBackupHelper(mUserId));
}
@Override
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 093ecd57124f..e12034333554 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -50,6 +50,8 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IUserManager;
+import android.os.Looper;
+import android.os.Message;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -74,6 +76,8 @@ import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
@@ -99,6 +103,22 @@ public class ClipboardService extends SystemService {
private static final boolean IS_EMULATOR =
SystemProperties.getBoolean("ro.boot.qemu", false);
+ @VisibleForTesting
+ public static final long DEFAULT_CLIPBOARD_TIMEOUT_MILLIS = 3600000;
+
+ /**
+ * Device config property for whether clipboard auto clear is enabled on the device
+ **/
+ public static final String PROPERTY_AUTO_CLEAR_ENABLED =
+ "auto_clear_enabled";
+
+ /**
+ * Device config property for time period in milliseconds after which clipboard is auto
+ * cleared
+ **/
+ public static final String PROPERTY_AUTO_CLEAR_TIMEOUT =
+ "auto_clear_timeout";
+
// DeviceConfig properties
private static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length";
private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400;
@@ -312,6 +332,10 @@ public class ClipboardService extends SystemService {
* 'intendingUserId' and the uid is called 'intendingUid'.
*/
private class ClipboardImpl extends IClipboard.Stub {
+
+ private final Handler mClipboardClearHandler = new ClipboardClearHandler(
+ mWorkerHandler.getLooper());
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -352,10 +376,34 @@ public class ClipboardService extends SystemService {
}
checkDataOwner(clip, intendingUid);
synchronized (mLock) {
+ scheduleAutoClear(userId);
setPrimaryClipInternalLocked(clip, intendingUid, sourcePackage);
}
}
+ private void scheduleAutoClear(@UserIdInt int userId) {
+ final long oldIdentity = Binder.clearCallingIdentity();
+ try {
+ if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
+ PROPERTY_AUTO_CLEAR_ENABLED, false)) {
+ mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
+ userId);
+ Message clearMessage = Message.obtain(mClipboardClearHandler,
+ ClipboardClearHandler.MSG_CLEAR, userId, 0, userId);
+ mClipboardClearHandler.sendMessageDelayed(clearMessage,
+ getTimeoutForAutoClear());
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldIdentity);
+ }
+ }
+
+ private long getTimeoutForAutoClear() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_CLIPBOARD,
+ PROPERTY_AUTO_CLEAR_TIMEOUT,
+ DEFAULT_CLIPBOARD_TIMEOUT_MILLIS);
+ }
+
@Override
public void clearPrimaryClip(String callingPackage, @UserIdInt int userId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
@@ -365,6 +413,8 @@ public class ClipboardService extends SystemService {
return;
}
synchronized (mLock) {
+ mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
+ userId);
setPrimaryClipInternalLocked(null, intendingUid, callingPackage);
}
}
@@ -391,6 +441,9 @@ public class ClipboardService extends SystemService {
PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);
notifyTextClassifierLocked(clipboard, pkg, intendingUid);
+ if (clipboard.primaryClip != null) {
+ scheduleAutoClear(userId);
+ }
return clipboard.primaryClip;
}
}
@@ -484,6 +537,32 @@ public class ClipboardService extends SystemService {
return null;
}
}
+
+ private class ClipboardClearHandler extends Handler {
+
+ public static final int MSG_CLEAR = 101;
+
+ ClipboardClearHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case MSG_CLEAR:
+ final int userId = msg.arg1;
+ synchronized (mLock) {
+ if (getClipboardLocked(userId).primaryClip != null) {
+ FrameworkStatsLog.write(FrameworkStatsLog.CLIPBOARD_CLEARED,
+ FrameworkStatsLog.CLIPBOARD_CLEARED__SOURCE__AUTO_CLEAR);
+ setPrimaryClipInternalLocked(null, Binder.getCallingUid(), null);
+ }
+ }
+ break;
+ default:
+ Slog.wtf(TAG, "ClipboardClearHandler received unknown message " + msg.what);
+ }
+ }
+ }
};
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
index 3bf6ca2dac0d..df95bf597dc0 100644
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ b/services/core/java/com/android/server/communal/CommunalManagerService.java
@@ -87,7 +87,7 @@ public final class CommunalManagerService extends SystemService {
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
if (!shouldIntercept(info.aInfo)) {
if (DEBUG) {
Slog.d(TAG, "Activity allowed, not intercepting: "
@@ -110,8 +110,10 @@ public final class CommunalManagerService extends SystemService {
PendingIntent.FLAG_IMMUTABLE,
/* bOptions= */ null);
- return LaunchAfterAuthenticationActivity.createLaunchAfterAuthenticationIntent(
- new IntentSender(target));
+ return new ActivityInterceptResult(
+ LaunchAfterAuthenticationActivity.createLaunchAfterAuthenticationIntent(
+ new IntentSender(target)),
+ info.checkedOptions);
}
};
diff --git a/services/core/java/com/android/server/display/DensityMap.java b/services/core/java/com/android/server/display/DensityMap.java
new file mode 100644
index 000000000000..4aafd148a6dd
--- /dev/null
+++ b/services/core/java/com/android/server/display/DensityMap.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Class which can compute the logical density for a display resolution. It holds a collection
+ * of pre-configured densities, which are used for look-up and interpolation.
+ */
+public class DensityMap {
+
+ // Instead of resolutions we store the squared diagonal size. Diagonals make the map
+ // keys invariant to rotations and are useful for interpolation because they're scalars.
+ // Squared diagonals have the same properties as diagonals (the square function is monotonic)
+ // but also allow us to use integer types and avoid floating point arithmetics.
+ private final Entry[] mSortedDensityMapEntries;
+
+ /**
+ * Creates a density map. The newly created object takes ownership of the passed array.
+ */
+ static DensityMap createByOwning(Entry[] densityMapEntries) {
+ return new DensityMap(densityMapEntries);
+ }
+
+ private DensityMap(Entry[] densityMapEntries) {
+ Arrays.sort(densityMapEntries, Comparator.comparingInt(entry -> entry.squaredDiagonal));
+ mSortedDensityMapEntries = densityMapEntries;
+ verifyDensityMap(mSortedDensityMapEntries);
+ }
+
+ /**
+ * Returns the logical density for the given resolution.
+ *
+ * If the resolution matches one of the entries in the map, the corresponding density is
+ * returned. Otherwise the return value is interpolated using the closest entries in the map.
+ */
+ public int getDensityForResolution(int width, int height) {
+ int squaredDiagonal = width * width + height * height;
+
+ // Search for two pre-configured entries "left" and "right" with the following criteria
+ // * left <= squaredDiagonal
+ // * squaredDiagonal - left is minimal
+ // * right > squaredDiagonal
+ // * right - squaredDiagonal is minimal
+ Entry left = Entry.ZEROES;
+ Entry right = null;
+
+ for (Entry entry : mSortedDensityMapEntries) {
+ if (entry.squaredDiagonal <= squaredDiagonal) {
+ left = entry;
+ } else {
+ right = entry;
+ break;
+ }
+ }
+
+ // Check if we found an exact match.
+ if (left.squaredDiagonal == squaredDiagonal) {
+ return left.density;
+ }
+
+ // If no configured resolution is higher than the specified resolution, interpolate
+ // between (0,0) and (maxConfiguredDiagonal, maxConfiguredDensity).
+ if (right == null) {
+ right = left; // largest entry in the sorted array
+ left = Entry.ZEROES;
+ }
+
+ double leftDiagonal = Math.sqrt(left.squaredDiagonal);
+ double rightDiagonal = Math.sqrt(right.squaredDiagonal);
+ double diagonal = Math.sqrt(squaredDiagonal);
+
+ return (int) Math.round((diagonal - leftDiagonal) * (right.density - left.density)
+ / (rightDiagonal - leftDiagonal) + left.density);
+ }
+
+ private static void verifyDensityMap(Entry[] sortedEntries) {
+ for (int i = 1; i < sortedEntries.length; i++) {
+ Entry prev = sortedEntries[i - 1];
+ Entry curr = sortedEntries[i];
+
+ if (prev.squaredDiagonal == curr.squaredDiagonal) {
+ // This will most often happen because there are two entries with the same
+ // resolution (AxB and AxB) or rotated resolution (AxB and BxA), but it can also
+ // happen in the very rare cases when two different resolutions happen to have
+ // the same diagonal (e.g. 100x700 and 500x500).
+ throw new IllegalStateException("Found two entries in the density map with"
+ + " the same diagonal: " + prev + ", " + curr);
+ } else if (prev.density > curr.density) {
+ throw new IllegalStateException("Found two entries in the density map with"
+ + " increasing diagonal but decreasing density: " + prev + ", " + curr);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DensityMap{"
+ + "mDensityMapEntries=" + Arrays.toString(mSortedDensityMapEntries)
+ + '}';
+ }
+
+ static class Entry {
+ public static final Entry ZEROES = new Entry(0, 0, 0);
+
+ public final int squaredDiagonal;
+ public final int density;
+
+ Entry(int width, int height, int density) {
+ this.squaredDiagonal = width * width + height * height;
+ this.density = density;
+ }
+
+ @Override
+ public String toString() {
+ return "DensityMapEntry{"
+ + "squaredDiagonal=" + squaredDiagonal
+ + ", density=" + density + '}';
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 2ae5cbbbf24b..a9e1647446cb 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -18,6 +18,7 @@ package com.android.server.display;
import android.annotation.NonNull;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
@@ -31,6 +32,7 @@ import android.view.DisplayAddress;
import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.display.config.Density;
import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.DisplayQuirks;
import com.android.server.display.config.HbmTiming;
@@ -52,6 +54,7 @@ import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -70,6 +73,8 @@ public class DisplayDeviceConfig {
private static final String ETC_DIR = "etc";
private static final String DISPLAY_CONFIG_DIR = "displayconfig";
private static final String CONFIG_FILE_FORMAT = "display_%s.xml";
+ private static final String DEFAULT_CONFIG_FILE = "default.xml";
+ private static final String DEFAULT_CONFIG_FILE_WITH_UIMODE_FORMAT = "default_%s.xml";
private static final String PORT_SUFFIX_FORMAT = "port_%d";
private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d";
private static final String NO_SUFFIX_FORMAT = "%d";
@@ -121,6 +126,7 @@ public class DisplayDeviceConfig {
private List<String> mQuirks;
private boolean mIsHighBrightnessModeEnabled = false;
private HighBrightnessModeData mHbmData;
+ private DensityMap mDensityMap;
private String mLoadedFrom = null;
private DisplayDeviceConfig(Context context) {
@@ -141,6 +147,33 @@ public class DisplayDeviceConfig {
*/
public static DisplayDeviceConfig create(Context context, long physicalDisplayId,
boolean isDefaultDisplay) {
+ final DisplayDeviceConfig config = createWithoutDefaultValues(context, physicalDisplayId,
+ isDefaultDisplay);
+
+ config.copyUninitializedValuesFromSecondaryConfig(loadDefaultConfigurationXml(context));
+ return config;
+ }
+
+ /**
+ * Creates an instance using global values since no display device config xml exists.
+ * Uses values from config or PowerManager.
+ *
+ * @param context
+ * @param useConfigXml
+ * @return A configuration instance.
+ */
+ public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
+ final DisplayDeviceConfig config;
+ if (useConfigXml) {
+ config = getConfigFromGlobalXml(context);
+ } else {
+ config = getConfigFromPmValues(context);
+ }
+ return config;
+ }
+
+ private static DisplayDeviceConfig createWithoutDefaultValues(Context context,
+ long physicalDisplayId, boolean isDefaultDisplay) {
DisplayDeviceConfig config;
config = loadConfigFromDirectory(context, Environment.getProductDirectory(),
@@ -161,22 +194,53 @@ public class DisplayDeviceConfig {
return create(context, isDefaultDisplay);
}
- /**
- * Creates an instance using global values since no display device config xml exists.
- * Uses values from config or PowerManager.
- *
- * @param context
- * @param useConfigXml
- * @return A configuration instance.
- */
- public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
- DisplayDeviceConfig config;
- if (useConfigXml) {
- config = getConfigFromGlobalXml(context);
- } else {
- config = getConfigFromPmValues(context);
+ private static DisplayConfiguration loadDefaultConfigurationXml(Context context) {
+ List<File> defaultXmlLocations = new ArrayList<>();
+ defaultXmlLocations.add(Environment.buildPath(Environment.getProductDirectory(),
+ ETC_DIR, DISPLAY_CONFIG_DIR, DEFAULT_CONFIG_FILE));
+ defaultXmlLocations.add(Environment.buildPath(Environment.getVendorDirectory(),
+ ETC_DIR, DISPLAY_CONFIG_DIR, DEFAULT_CONFIG_FILE));
+
+ // Read config_defaultUiModeType directly because UiModeManager hasn't started yet.
+ final int uiModeType = context.getResources()
+ .getInteger(com.android.internal.R.integer.config_defaultUiModeType);
+ final String uiModeTypeStr = Configuration.getUiModeTypeString(uiModeType);
+ if (uiModeTypeStr != null) {
+ defaultXmlLocations.add(Environment.buildPath(Environment.getRootDirectory(),
+ ETC_DIR, DISPLAY_CONFIG_DIR,
+ String.format(DEFAULT_CONFIG_FILE_WITH_UIMODE_FORMAT, uiModeTypeStr)));
}
- return config;
+ defaultXmlLocations.add(Environment.buildPath(Environment.getRootDirectory(),
+ ETC_DIR, DISPLAY_CONFIG_DIR, DEFAULT_CONFIG_FILE));
+
+ final File configFile = getFirstExistingFile(defaultXmlLocations);
+ if (configFile == null) {
+ // Display configuration files aren't required to exist.
+ return null;
+ }
+
+ DisplayConfiguration defaultConfig = null;
+
+ try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
+ defaultConfig = XmlParser.read(in);
+ if (defaultConfig == null) {
+ Slog.i(TAG, "Default DisplayDeviceConfig file is null");
+ }
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.e(TAG, "Encountered an error while reading/parsing display config file: "
+ + configFile, e);
+ }
+
+ return defaultConfig;
+ }
+
+ private static File getFirstExistingFile(Collection<File> files) {
+ for (File file : files) {
+ if (file.exists() && file.isFile()) {
+ return file;
+ }
+ }
+ return null;
}
private static DisplayDeviceConfig loadConfigFromDirectory(Context context,
@@ -316,9 +380,13 @@ public class DisplayDeviceConfig {
return mRefreshRateLimitations;
}
+ public DensityMap getDensityMap() {
+ return mDensityMap;
+ }
+
@Override
public String toString() {
- String str = "DisplayDeviceConfig{"
+ return "DisplayDeviceConfig{"
+ "mLoadedFrom=" + mLoadedFrom
+ ", mBacklight=" + Arrays.toString(mBacklight)
+ ", mNits=" + Arrays.toString(mNits)
@@ -340,8 +408,8 @@ public class DisplayDeviceConfig {
+ ", mAmbientLightSensor=" + mAmbientLightSensor
+ ", mProximitySensor=" + mProximitySensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
+ + ", mDensityMap= " + mDensityMap
+ "}";
- return str;
}
private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
@@ -384,6 +452,7 @@ public class DisplayDeviceConfig {
try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
final DisplayConfiguration config = XmlParser.read(in);
if (config != null) {
+ loadDensityMap(config);
loadBrightnessDefaultFromDdcXml(config);
loadBrightnessConstraintsFromConfigXml();
loadBrightnessMap(config);
@@ -429,6 +498,35 @@ public class DisplayDeviceConfig {
setProxSensorUnspecified();
}
+ private void copyUninitializedValuesFromSecondaryConfig(DisplayConfiguration defaultConfig) {
+ if (defaultConfig == null) {
+ return;
+ }
+
+ if (mDensityMap == null) {
+ loadDensityMap(defaultConfig);
+ }
+ }
+
+ private void loadDensityMap(DisplayConfiguration config) {
+ if (config.getDensityMap() == null) {
+ return;
+ }
+
+ final List<Density> entriesFromXml = config.getDensityMap().getDensity();
+
+ final DensityMap.Entry[] entries =
+ new DensityMap.Entry[entriesFromXml.size()];
+ for (int i = 0; i < entriesFromXml.size(); i++) {
+ final Density density = entriesFromXml.get(i);
+ entries[i] = new DensityMap.Entry(
+ density.getWidth().intValue(),
+ density.getHeight().intValue(),
+ density.getDensity().intValue());
+ }
+ mDensityMap = DensityMap.createByOwning(entries);
+ }
+
private void loadBrightnessDefaultFromDdcXml(DisplayConfiguration config) {
// Default brightness values are stored in the displayDeviceConfig file,
// Or we fallback standard values if not.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 4c569996817d..c0a6abf3a121 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -45,7 +45,10 @@ import android.app.compat.CompatChanges;
import android.companion.virtual.IVirtualDevice;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
@@ -421,6 +424,32 @@ public final class DisplayManagerService extends SystemService {
// Receives notifications about changes to Settings.
private SettingsObserver mSettingsObserver;
+ // Keeps note of what state the device is in, used for idle screen brightness mode.
+ private boolean mIsDocked;
+ private boolean mIsDreaming;
+
+ private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final DisplayManagerInternal dmi =
+ LocalServices.getService(DisplayManagerInternal.class);
+ if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) {
+ int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+ Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ mIsDocked = dockState == Intent.EXTRA_DOCK_STATE_DESK
+ || dockState == Intent.EXTRA_DOCK_STATE_LE_DESK
+ || dockState == Intent.EXTRA_DOCK_STATE_HE_DESK;
+ }
+ if (Intent.ACTION_DREAMING_STARTED.equals(intent.getAction())) {
+ mIsDreaming = true;
+ } else if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) {
+ mIsDreaming = false;
+ }
+ setDockedAndIdleEnabled(/* enabled= */(mIsDocked && mIsDreaming),
+ Display.DEFAULT_DISPLAY);
+ }
+ };
+
private final boolean mAllowNonNativeRefreshRateOverride;
private final BrightnessSynchronizer mBrightnessSynchronizer;
@@ -616,6 +645,13 @@ public final class DisplayManagerService extends SystemService {
mSettingsObserver = new SettingsObserver();
mBrightnessSynchronizer.startSynchronizing();
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DREAMING_STARTED);
+ filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+ filter.addAction(Intent.ACTION_DOCK_EVENT);
+
+ mContext.registerReceiver(mIdleModeReceiver, filter);
}
@VisibleForTesting
@@ -2096,6 +2132,16 @@ public final class DisplayManagerService extends SystemService {
}
}
+ void setDockedAndIdleEnabled(boolean enabled, int displayId) {
+ synchronized (mSyncRoot) {
+ final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
+ displayId);
+ if (displayPowerController != null) {
+ displayPowerController.setAutomaticScreenBrightnessMode(enabled);
+ }
+ }
+ }
+
private void clearViewportsLocked() {
mViewports.clear();
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 43a850c5d626..9a7ddcb2ff91 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -76,6 +76,10 @@ class DisplayManagerShellCommand extends ShellCommand {
return setUserDisabledHdrTypes();
case "get-user-disabled-hdr-types":
return getUserDisabledHdrTypes();
+ case "dock":
+ return setDockedAndIdle();
+ case "undock":
+ return unsetDockedAndIdle();
default:
return handleDefaultCommands(cmd);
}
@@ -124,6 +128,10 @@ class DisplayManagerShellCommand extends ShellCommand {
pw.println(" Sets the user disabled HDR types as TYPES");
pw.println(" get-user-disabled-hdr-types");
pw.println(" Returns the user disabled HDR types");
+ pw.println(" dock");
+ pw.println(" Sets brightness to docked + idle screen brightness mode");
+ pw.println(" undock");
+ pw.println(" Sets brightness to active (normal) screen brightness mode");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -345,4 +353,14 @@ class DisplayManagerShellCommand extends ShellCommand {
return -1;
}
}
+
+ private int setDockedAndIdle() {
+ mService.setDockedAndIdleEnabled(true, Display.DEFAULT_DISPLAY);
+ return 0;
+ }
+
+ private int unsetDockedAndIdle() {
+ mService.setDockedAndIdleEnabled(false, Display.DEFAULT_DISPLAY);
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index c82b16d77772..efd2f6f1bfe1 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -41,7 +41,6 @@ import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -619,10 +618,11 @@ public class DisplayModeDirector {
mAppRequestObserver.dumpLocked(pw);
mBrightnessObserver.dumpLocked(pw);
mUdfpsObserver.dumpLocked(pw);
- mSensorObserver.dumpLocked(pw);
mHbmObserver.dumpLocked(pw);
mSkinThermalStatusObserver.dumpLocked(pw);
}
+
+ mSensorObserver.dump(pw);
}
private void updateVoteLocked(int priority, Vote vote) {
@@ -2244,7 +2244,7 @@ public class DisplayModeDirector {
}
}
- void dumpLocked(PrintWriter pw) {
+ void dump(PrintWriter pw) {
pw.println(" SensorObserver");
synchronized (mSensorObserverLock) {
pw.println(" mIsProxActive=" + mIsProxActive);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index b46ee27d30cb..2dfaa8bad380 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -395,11 +395,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
@Nullable
private BrightnessMappingStrategy mIdleModeBrightnessMapper;
- // If these are both true, and mIdleModeBrightnessMapper != null,
- // then we are in idle screen brightness mode.
- private boolean mIsDreaming;
- private boolean mIsDocked;
-
// The current brightness configuration.
@Nullable
private BrightnessConfiguration mBrightnessConfiguration;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b6d13e0c5bbf..300f59ee1dd4 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -426,6 +426,15 @@ final class LocalDisplayAdapter extends DisplayAdapter {
: mDefaultModeId;
}
+ private int getLogicalDensity() {
+ DensityMap densityMap = getDisplayDeviceConfig().getDensityMap();
+ if (densityMap == null) {
+ return (int) (mStaticDisplayInfo.density * 160 + 0.5);
+ }
+
+ return densityMap.getDensityForResolution(mInfo.width, mInfo.height);
+ }
+
private void loadDisplayDeviceConfig() {
// Load display device config
final Context context = getOverlayContext();
@@ -591,7 +600,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
mInfo.address = physicalAddress;
- mInfo.densityDpi = (int) (mStaticDisplayInfo.density * 160 + 0.5f);
+ mInfo.densityDpi = getLogicalDensity();
mInfo.xDpi = mActiveSfDisplayMode.xDpi;
mInfo.yDpi = mActiveSfDisplayMode.yDpi;
mInfo.deviceProductInfo = mStaticDisplayInfo.deviceProductInfo;
@@ -1029,7 +1038,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
for (int i = 0; i < mSupportedModes.size(); i++) {
pw.println(" " + mSupportedModes.valueAt(i));
}
- pw.println("mSupportedColorModes=" + mSupportedColorModes.toString());
+ pw.println("mSupportedColorModes=" + mSupportedColorModes);
pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 3d04037185f3..261aa32f093e 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -347,6 +347,7 @@ public class InputManagerService extends IInputManager.Stub
private static native boolean nativeEnableSensor(long ptr, int deviceId, int sensorType,
int samplingPeriodUs, int maxBatchReportLatencyUs);
private static native void nativeDisableSensor(long ptr, int deviceId, int sensorType);
+ private static native void nativeCancelCurrentTouch(long ptr);
// Maximum number of milliseconds to wait for input event injection.
private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
@@ -2510,6 +2511,16 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
+ public void cancelCurrentTouch() {
+ if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
+ "cancelCurrentTouch()")) {
+ throw new SecurityException("Requires MONITOR_INPUT permission");
+ }
+
+ nativeCancelCurrentTouch(mPtr);
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 3d91feef7043..c5dc23e30822 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -82,6 +82,7 @@ final class InputMethodBindingController {
private IBinder mCurToken;
private int mCurSeq;
private boolean mVisibleBound;
+ private boolean mSupportsStylusHw;
/**
* Binding flags for establishing connection to the {@link InputMethodService}.
@@ -295,6 +296,10 @@ final class InputMethodBindingController {
mService.scheduleNotifyImeUidToAudioService(mCurMethodUid);
mService.reRequestCurrentClientSessionLocked();
}
+ mSupportsStylusHw = mMethodMap.get(mSelectedMethodId).supportsStylusHandwriting();
+ if (mSupportsStylusHw) {
+ // TODO init Handwriting spy.
+ }
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index c1d8e78e3cd3..e696d1ec6433 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -33,6 +33,7 @@ import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF
import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+import static android.os.UserHandle.USER_CURRENT;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.location.LocationManagerService.D;
@@ -893,6 +894,10 @@ public class LocationProviderManager extends
MAX_FASTEST_INTERVAL_JITTER_MS);
if (deltaMs
< getRequest().getMinUpdateIntervalMillis() - maxJitterMs) {
+ if (D) {
+ Log.v(TAG, mName + " provider registration " + getIdentity()
+ + " dropped delivery - too fast");
+ }
return false;
}
@@ -902,6 +907,10 @@ public class LocationProviderManager extends
if (smallestDisplacementM > 0.0 && location.distanceTo(
mPreviousLocation)
<= smallestDisplacementM) {
+ if (D) {
+ Log.v(TAG, mName + " provider registration " + getIdentity()
+ + " dropped delivery - too close");
+ }
return false;
}
}
@@ -919,7 +928,8 @@ public class LocationProviderManager extends
if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(getPermissionLevel()),
getIdentity())) {
if (D) {
- Log.w(TAG, "noteOp denied for " + getIdentity());
+ Log.w(TAG,
+ mName + " provider registration " + getIdentity() + " noteOp denied");
}
return null;
}
@@ -1503,7 +1513,7 @@ public class LocationProviderManager extends
public boolean isEnabled(int userId) {
if (userId == UserHandle.USER_NULL) {
return false;
- } else if (userId == UserHandle.USER_CURRENT) {
+ } else if (userId == USER_CURRENT) {
return isEnabled(mUserHelper.getCurrentUserId());
}
@@ -1676,7 +1686,7 @@ public class LocationProviderManager extends
}
}
return lastLocation;
- } else if (userId == UserHandle.USER_CURRENT) {
+ } else if (userId == USER_CURRENT) {
return getLastLocationUnsafe(mUserHelper.getCurrentUserId(), permissionLevel,
isBypass, maximumAgeMs);
}
@@ -1721,7 +1731,7 @@ public class LocationProviderManager extends
setLastLocation(location, runningUserIds[i]);
}
return;
- } else if (userId == UserHandle.USER_CURRENT) {
+ } else if (userId == USER_CURRENT) {
setLastLocation(location, mUserHelper.getCurrentUserId());
return;
}
@@ -2404,13 +2414,13 @@ public class LocationProviderManager extends
filtered = locationResult.filter(location -> {
if (!location.isMock()) {
if (location.getLatitude() == 0 && location.getLongitude() == 0) {
- Log.w(TAG, "blocking 0,0 location from " + mName + " provider");
+ Log.e(TAG, "blocking 0,0 location from " + mName + " provider");
return false;
}
}
if (!location.isComplete()) {
- Log.w(TAG, "blocking incomplete location from " + mName + " provider");
+ Log.e(TAG, "blocking incomplete location from " + mName + " provider");
return false;
}
@@ -2428,6 +2438,12 @@ public class LocationProviderManager extends
filtered = locationResult;
}
+ Location last = getLastLocationUnsafe(USER_CURRENT, PERMISSION_FINE, true, Long.MAX_VALUE);
+ if (last != null && locationResult.get(0).getElapsedRealtimeNanos()
+ < last.getElapsedRealtimeNanos()) {
+ Log.e(TAG, "non-monotonic location received from " + mName + " provider");
+ }
+
// update last location
setLastLocation(filtered.getLastLocation(), UserHandle.USER_ALL);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index bf50db85d984..a860d35b83dc 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2436,7 +2436,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
networkId, templateMeteredness, NetworkStats.ROAMING_ALL,
NetworkStats.DEFAULT_NETWORK_ALL, NetworkTemplate.NETWORK_TYPE_ALL,
NetworkTemplate.OEM_MANAGED_ALL, subscriberIdMatchRule);
- if (template.isPersistable()) {
+ if (NetworkPolicy.isTemplatePersistable(template)) {
mNetworkPolicy.put(template, new NetworkPolicy(template, cycleRule,
warningBytes, limitBytes, lastWarningSnooze,
lastLimitSnooze, metered, inferred));
@@ -2643,7 +2643,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
for (int i = 0; i < mNetworkPolicy.size(); i++) {
final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
final NetworkTemplate template = policy.template;
- if (!template.isPersistable()) continue;
+ if (!NetworkPolicy.isTemplatePersistable(template)) continue;
out.startTag(null, TAG_NETWORK_POLICY);
writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, template.getMatchRule());
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 137fc85c1e72..20686322fd3d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7871,7 +7871,9 @@ public class NotificationManagerService extends SystemService {
int index = mToastQueue.indexOf(record);
if (index >= 0) {
- mToastQueue.remove(index);
+ ToastRecord toast = mToastQueue.remove(index);
+ mWindowManagerInternal.removeWindowToken(
+ toast.windowToken, true /* removeWindows */, toast.displayId);
}
record = (mToastQueue.size() > 0) ? mToastQueue.get(0) : null;
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 7d4877cdc7ee..258ae8c1c849 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -2151,6 +2151,10 @@ public class PreferencesHelper implements RankingConfig {
final PackagePreferences r = mPackagePreferences.valueAt(i);
event.writeInt(r.uid);
event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
+
+ // collect whether this package's importance info was user-set for later, if needed
+ // before the migration is enabled, this will simply default to false in all cases.
+ boolean importanceIsUserSet = false;
if (mPermissionHelper.isMigrationEnabled()) {
// Even if this package's data is not present, we need to write something;
// so default to IMPORTANCE_NONE, since if PM doesn't know about the package
@@ -2158,8 +2162,12 @@ public class PreferencesHelper implements RankingConfig {
int importance = IMPORTANCE_NONE;
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
- importance = pkgPermissions.get(key).first
+ Pair<Boolean, Boolean> permissionPair = pkgPermissions.get(key);
+ importance = permissionPair.first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE;
+ // cache the second value for writing later
+ importanceIsUserSet = permissionPair.second;
+
pkgsWithPermissionsToHandle.remove(key);
}
event.writeInt(importance);
@@ -2168,6 +2176,7 @@ public class PreferencesHelper implements RankingConfig {
}
event.writeInt(r.visibility);
event.writeInt(r.lockedAppFields);
+ event.writeBoolean(importanceIsUserSet); // optional bool user_set_importance = 5;
events.add(event.build());
}
}
@@ -2189,6 +2198,7 @@ public class PreferencesHelper implements RankingConfig {
// builder
event.writeInt(DEFAULT_VISIBILITY);
event.writeInt(DEFAULT_LOCKED_APP_FIELDS);
+ event.writeBoolean(pkgPermissions.get(p).second); // user_set_importance field
events.add(event.build());
}
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index fb77d101f910..05567480e6bf 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -34,6 +34,7 @@ import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.component.ParsedApexSystemService;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.Binder;
@@ -53,6 +54,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.modules.utils.build.UnboundedSdkLevel;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.ParsedPackage;
@@ -411,6 +413,11 @@ public abstract class ApexManager {
throws PackageManagerException;
/**
+ * Get a map of system services defined in an apex mapped to the jar files they reside in.
+ */
+ public abstract Map<String, String> getApexSystemServices();
+
+ /**
* Dumps various state information to the provided {@link PrintWriter} object.
*
* @param pw the {@link PrintWriter} object to send information to.
@@ -438,6 +445,12 @@ public abstract class ApexManager {
private Set<ActiveApexInfo> mActiveApexInfosCache;
/**
+ * Map of all apex system services to the jar files they are contained in.
+ */
+ @GuardedBy("mLock")
+ private Map<String, String> mApexSystemServices = new ArrayMap<>();
+
+ /**
* Contains the list of {@code packageName}s of apks-in-apex for given
* {@code apexModuleName}. See {@link #mPackageNameToApexModuleName} to understand the
* difference between {@code packageName} and {@code apexModuleName}.
@@ -573,6 +586,32 @@ public abstract class ApexManager {
+ ai.modulePath);
}
mAllPackagesCache.add(packageInfo);
+ for (ParsedApexSystemService service :
+ parseResult.parsedPackage.getApexSystemServices()) {
+ String minSdkVersion = service.getMinSdkVersion();
+ if (minSdkVersion != null && !UnboundedSdkLevel.isAtLeast(minSdkVersion)) {
+ Slog.d(TAG, String.format(
+ "ApexSystemService %s with min_sdk_version=%s is skipped",
+ service.getName(), service.getMinSdkVersion()));
+ continue;
+ }
+ String maxSdkVersion = service.getMaxSdkVersion();
+ if (maxSdkVersion != null && !UnboundedSdkLevel.isAtMost(maxSdkVersion)) {
+ Slog.d(TAG, String.format(
+ "ApexSystemService %s with max_sdk_version=%s is skipped",
+ service.getName(), service.getMaxSdkVersion()));
+ continue;
+ }
+
+ String name = service.getName();
+ if (mApexSystemServices.containsKey(name)) {
+ throw new IllegalStateException(String.format(
+ "Duplicate apex-system-service %s from %s, %s",
+ name, mApexSystemServices.get(name), service.getJarPath()));
+ }
+
+ mApexSystemServices.put(name, service.getJarPath());
+ }
mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName);
if (ai.isActive) {
if (activePackagesSet.contains(packageInfo.packageName)) {
@@ -1092,6 +1131,15 @@ public abstract class ApexManager {
}
}
+ @Override
+ public Map<String, String> getApexSystemServices() {
+ synchronized (mLock) {
+ Preconditions.checkState(mApexSystemServices != null,
+ "APEX packages have not been scanned");
+ return mApexSystemServices;
+ }
+ }
+
/**
* Dump information about the packages contained in a particular cache
* @param packagesCache the cache to print information about.
@@ -1370,6 +1418,13 @@ public abstract class ApexManager {
}
@Override
+ public Map<String, String> getApexSystemServices() {
+ // TODO(satayev): we can't really support flattened apex use case, and need to migrate
+ // the manifest entries into system's manifest asap.
+ return Collections.emptyMap();
+ }
+
+ @Override
void dump(PrintWriter pw, String packageName) {
// No-op
}
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index e99512d3b9fa..bedb8b9f7a18 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -74,13 +74,6 @@ final class AppDataHelper {
mArtManagerService = mInjector.getArtManagerService();
}
- AppDataHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
- mPm = pm;
- mInjector = injector;
- mInstaller = injector.getInstaller();
- mArtManagerService = injector.getArtManagerService();
- }
-
/**
* Prepare app data for the given app just after it was installed or
* upgraded. This method carefully only touches users that it's installed
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 8fda109a83ce..e84d990b5d21 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -236,7 +236,9 @@ final class DeletePackageHelper {
if (res) {
final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
info.sendPackageRemovedBroadcasts(killApp, removedBySystem);
- info.sendSystemPackageUpdatedBroadcasts();
+ if (disabledSystemPs != null) {
+ info.sendSystemPackageUpdatedBroadcasts(disabledSystemPs.getAppId());
+ }
}
// Force a gc here.
Runtime.getRuntime().gc();
@@ -591,6 +593,7 @@ final class DeletePackageHelper {
if (outInfo != null) {
// Delete the updated package
outInfo.mIsRemovedPackageSystemUpdate = true;
+ outInfo.mAppIdChanging = disabledPs.getAppId() != deletedPs.getAppId();
}
if (disabledPs.getVersionCode() < deletedPs.getVersionCode()
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index c8594eba69a6..8573585c3c6b 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -32,8 +32,6 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
@@ -47,7 +45,6 @@ import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
-import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
@@ -56,6 +53,8 @@ import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
+import static com.android.server.pm.PackageManagerService.DEBUG_UPGRADE;
+import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.pm.PackageManagerService.POST_INSTALL;
@@ -73,13 +72,16 @@ import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
import static com.android.server.pm.PackageManagerService.SCAN_IGNORE_FROZEN;
import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
+import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
@@ -89,6 +91,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
import android.app.ApplicationPackageManager;
import android.app.backup.IBackupManager;
import android.content.ContentResolver;
@@ -125,7 +128,6 @@ import android.os.Environment;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
@@ -145,14 +147,15 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
-import com.android.server.Watchdog;
+import com.android.server.pm.dex.ArtManagerService;
+import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.pm.dex.ViewCompiler;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -190,271 +193,45 @@ import java.util.concurrent.ExecutorService;
final class InstallPackageHelper {
private final PackageManagerService mPm;
private final AppDataHelper mAppDataHelper;
- private final PackageManagerServiceInjector mInjector;
private final BroadcastHelper mBroadcastHelper;
private final RemovePackageHelper mRemovePackageHelper;
- private final ScanPackageHelper mScanPackageHelper;
+ private final StorageManager mStorageManager;
+ private final RollbackManagerInternal mRollbackManager;
+ private final IncrementalManager mIncrementalManager;
+ private final ApexManager mApexManager;
+ private final DexManager mDexManager;
+ private final ArtManagerService mArtManagerService;
+ private final AppOpsManager mAppOpsManager;
+ private final Context mContext;
+ private final PackageDexOptimizer mPackageDexOptimizer;
+ private final PackageAbiHelper mPackageAbiHelper;
+ private final ViewCompiler mViewCompiler;
+ private final IBackupManager mIBackupManager;
// TODO(b/198166813): remove PMS dependency
InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
mPm = pm;
- mInjector = pm.mInjector;
mAppDataHelper = appDataHelper;
- mBroadcastHelper = new BroadcastHelper(mInjector);
+ mBroadcastHelper = new BroadcastHelper(pm.mInjector);
mRemovePackageHelper = new RemovePackageHelper(pm);
- mScanPackageHelper = new ScanPackageHelper(pm);
+ mStorageManager = pm.mInjector.getSystemService(StorageManager.class);
+ mRollbackManager = pm.mInjector.getLocalService(RollbackManagerInternal.class);
+ mIncrementalManager = pm.mInjector.getIncrementalManager();
+ mApexManager = pm.mInjector.getApexManager();
+ mDexManager = pm.mInjector.getDexManager();
+ mArtManagerService = pm.mInjector.getArtManagerService();
+ mAppOpsManager = pm.mInjector.getSystemService(AppOpsManager.class);
+ mContext = pm.mInjector.getContext();
+ mPackageDexOptimizer = pm.mInjector.getPackageDexOptimizer();
+ mPackageAbiHelper = pm.mInjector.getAbiHelper();
+ mViewCompiler = pm.mInjector.getViewCompiler();
+ mIBackupManager = pm.mInjector.getIBackupManager();
}
InstallPackageHelper(PackageManagerService pm) {
this(pm, new AppDataHelper(pm));
}
- InstallPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
- mPm = pm;
- mInjector = injector;
- mAppDataHelper = new AppDataHelper(pm, mInjector);
- mBroadcastHelper = new BroadcastHelper(injector);
- mRemovePackageHelper = new RemovePackageHelper(pm);
- mScanPackageHelper = new ScanPackageHelper(pm);
- }
-
- @GuardedBy("mPm.mLock")
- public Map<String, ReconciledPackage> reconcilePackagesLocked(
- final ReconcileRequest request, KeySetManagerService ksms,
- PackageManagerServiceInjector injector)
- throws ReconcileFailure {
- final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
-
- final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
-
- // make a copy of the existing set of packages so we can combine them with incoming packages
- final ArrayMap<String, AndroidPackage> combinedPackages =
- new ArrayMap<>(request.mAllPackages.size() + scannedPackages.size());
-
- combinedPackages.putAll(request.mAllPackages);
-
- final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
- new ArrayMap<>();
-
- for (String installPackageName : scannedPackages.keySet()) {
- final ScanResult scanResult = scannedPackages.get(installPackageName);
-
- // add / replace existing with incoming packages
- combinedPackages.put(scanResult.mPkgSetting.getPackageName(),
- scanResult.mRequest.mParsedPackage);
-
- // in the first pass, we'll build up the set of incoming shared libraries
- final List<SharedLibraryInfo> allowedSharedLibInfos =
- SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
- request.mSharedLibrarySource);
- if (allowedSharedLibInfos != null) {
- for (SharedLibraryInfo info : allowedSharedLibInfos) {
- if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
- incomingSharedLibraries, info)) {
- throw new ReconcileFailure("Shared Library " + info.getName()
- + " is being installed twice in this set!");
- }
- }
- }
-
- // the following may be null if we're just reconciling on boot (and not during install)
- final InstallArgs installArgs = request.mInstallArgs.get(installPackageName);
- final PackageInstalledInfo res = request.mInstallResults.get(installPackageName);
- final PrepareResult prepareResult = request.mPreparedPackages.get(installPackageName);
- final boolean isInstall = installArgs != null;
- if (isInstall && (res == null || prepareResult == null)) {
- throw new ReconcileFailure("Reconcile arguments are not balanced for "
- + installPackageName + "!");
- }
-
- final DeletePackageAction deletePackageAction;
- // we only want to try to delete for non system apps
- if (isInstall && prepareResult.mReplace && !prepareResult.mSystem) {
- final boolean killApp = (scanResult.mRequest.mScanFlags & SCAN_DONT_KILL_APP) == 0;
- final int deleteFlags = PackageManager.DELETE_KEEP_DATA
- | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
- deletePackageAction = DeletePackageHelper.mayDeletePackageLocked(res.mRemovedInfo,
- prepareResult.mOriginalPs, prepareResult.mDisabledPs,
- deleteFlags, null /* all users */);
- if (deletePackageAction == null) {
- throw new ReconcileFailure(
- PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
- "May not delete " + installPackageName + " to replace");
- }
- } else {
- deletePackageAction = null;
- }
-
- final int scanFlags = scanResult.mRequest.mScanFlags;
- final int parseFlags = scanResult.mRequest.mParseFlags;
- final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
-
- final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting;
- final PackageSetting lastStaticSharedLibSetting =
- request.mLastStaticSharedLibSettings.get(installPackageName);
- final PackageSetting signatureCheckPs =
- (prepareResult != null && lastStaticSharedLibSetting != null)
- ? lastStaticSharedLibSetting
- : scanResult.mPkgSetting;
- boolean removeAppKeySetData = false;
- boolean sharedUserSignaturesChanged = false;
- SigningDetails signingDetails = null;
- if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
- if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
- // We just determined the app is signed correctly, so bring
- // over the latest parsed certs.
- } else {
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "Package " + parsedPackage.getPackageName()
- + " upgrade keys do not match the previously installed"
- + " version");
- } else {
- String msg = "System package " + parsedPackage.getPackageName()
- + " signature changed; retaining data.";
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- }
- }
- signingDetails = parsedPackage.getSigningDetails();
- } else {
- try {
- final Settings.VersionInfo versionInfo =
- request.mVersionInfos.get(installPackageName);
- final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
- final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
- final boolean isRollback = installArgs != null
- && installArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
- final boolean compatMatch = verifySignatures(signatureCheckPs,
- disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
- compareRecover, isRollback);
- // The new KeySets will be re-added later in the scanning process.
- if (compatMatch) {
- removeAppKeySetData = true;
- }
- // We just determined the app is signed correctly, so bring
- // over the latest parsed certs.
- signingDetails = parsedPackage.getSigningDetails();
-
- // if this is is a sharedUser, check to see if the new package is signed by a
- // newer
- // signing certificate than the existing one, and if so, copy over the new
- // details
- if (signatureCheckPs.getSharedUser() != null) {
- // Attempt to merge the existing lineage for the shared SigningDetails with
- // the lineage of the new package; if the shared SigningDetails are not
- // returned this indicates the new package added new signers to the lineage
- // and/or changed the capabilities of existing signers in the lineage.
- SigningDetails sharedSigningDetails =
- signatureCheckPs.getSharedUser().signatures.mSigningDetails;
- SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
- signingDetails);
- if (mergedDetails != sharedSigningDetails) {
- signatureCheckPs.getSharedUser().signatures.mSigningDetails =
- mergedDetails;
- }
- if (signatureCheckPs.getSharedUser().signaturesChanged == null) {
- signatureCheckPs.getSharedUser().signaturesChanged = Boolean.FALSE;
- }
- }
- } catch (PackageManagerException e) {
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- throw new ReconcileFailure(e);
- }
- signingDetails = parsedPackage.getSigningDetails();
-
- // If the system app is part of a shared user we allow that shared user to
- // change
- // signatures as well as part of an OTA. We still need to verify that the
- // signatures
- // are consistent within the shared user for a given boot, so only allow
- // updating
- // the signatures on the first package scanned for the shared user (i.e. if the
- // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
- if (signatureCheckPs.getSharedUser() != null) {
- final Signature[] sharedUserSignatures = signatureCheckPs.getSharedUser()
- .signatures.mSigningDetails.getSignatures();
- if (signatureCheckPs.getSharedUser().signaturesChanged != null
- && compareSignatures(sharedUserSignatures,
- parsedPackage.getSigningDetails().getSignatures())
- != PackageManager.SIGNATURE_MATCH) {
- if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
- // Mismatched signatures is an error and silently skipping system
- // packages will likely break the device in unforeseen ways.
- // However, we allow the device to boot anyway because, prior to Q,
- // vendors were not expecting the platform to crash in this
- // situation.
- // This WILL be a hard failure on any new API levels after Q.
- throw new ReconcileFailure(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
- "Signature mismatch for shared user: "
- + scanResult.mPkgSetting.getSharedUser());
- } else {
- // Treat mismatched signatures on system packages using a shared
- // UID as
- // fatal for the system overall, rather than just failing to install
- // whichever package happened to be scanned later.
- throw new IllegalStateException(
- "Signature mismatch on system package "
- + parsedPackage.getPackageName()
- + " for shared user "
- + scanResult.mPkgSetting.getSharedUser());
- }
- }
-
- sharedUserSignaturesChanged = true;
- signatureCheckPs.getSharedUser().signatures.mSigningDetails =
- parsedPackage.getSigningDetails();
- signatureCheckPs.getSharedUser().signaturesChanged = Boolean.TRUE;
- }
- // File a report about this.
- String msg = "System package " + parsedPackage.getPackageName()
- + " signature changed; retaining data.";
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- } catch (IllegalArgumentException e) {
- // should never happen: certs matched when checking, but not when comparing
- // old to new for sharedUser
- throw new RuntimeException(
- "Signing certificates comparison made on incomparable signing details"
- + " but somehow passed verifySignatures!", e);
- }
- }
-
- result.put(installPackageName,
- new ReconciledPackage(request, installArgs, scanResult.mPkgSetting,
- res, request.mPreparedPackages.get(installPackageName), scanResult,
- deletePackageAction, allowedSharedLibInfos, signingDetails,
- sharedUserSignaturesChanged, removeAppKeySetData));
- }
-
- for (String installPackageName : scannedPackages.keySet()) {
- // Check all shared libraries and map to their actual file path.
- // We only do this here for apps not on a system dir, because those
- // are the only ones that can fail an install due to this. We
- // will take care of the system apps by updating all of their
- // library paths after the scan is done. Also during the initial
- // scan don't update any libs as we do this wholesale after all
- // apps are scanned to avoid dependency based scanning.
- final ScanResult scanResult = scannedPackages.get(installPackageName);
- if ((scanResult.mRequest.mScanFlags & SCAN_BOOTING) != 0
- || (scanResult.mRequest.mParseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
- != 0) {
- continue;
- }
- try {
- result.get(installPackageName).mCollectedSharedLibraryInfos =
- SharedLibraryHelper.collectSharedLibraryInfos(
- scanResult.mRequest.mParsedPackage,
- combinedPackages, request.mSharedLibrarySource,
- incomingSharedLibraries, injector.getCompatibility());
-
- } catch (PackageManagerException e) {
- throw new ReconcileFailure(e.error, e.getMessage());
- }
- }
-
- return result;
- }
-
/**
* Commits the package scan and modifies system state.
* <p><em>WARNING:</em> The method may throw an exception in the middle
@@ -671,7 +448,7 @@ final class InstallPackageHelper {
// Add the new setting to mPackages
mPm.mPackages.put(pkg.getPackageName(), pkg);
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
- mPm.mApexManager.registerApkInApex(pkg);
+ mApexManager.registerApkInApex(pkg);
}
// Add the package's KeySets to the global KeySetManagerService
@@ -722,27 +499,6 @@ final class InstallPackageHelper {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- /**
- * If the database version for this type of package (internal storage or
- * external storage) is less than the version where package signatures
- * were updated, return true.
- */
- public boolean isCompatSignatureUpdateNeeded(AndroidPackage pkg) {
- return isCompatSignatureUpdateNeeded(mPm.getSettingsVersionForPackage(pkg));
- }
-
- public static boolean isCompatSignatureUpdateNeeded(Settings.VersionInfo ver) {
- return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_END_ENTITY;
- }
-
- public boolean isRecoverSignatureUpdateNeeded(AndroidPackage pkg) {
- return isRecoverSignatureUpdateNeeded(mPm.getSettingsVersionForPackage(pkg));
- }
-
- public static boolean isRecoverSignatureUpdateNeeded(Settings.VersionInfo ver) {
- return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
- }
-
public int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId,
@PackageManager.InstallFlags int installFlags,
@PackageManager.InstallReason int installReason,
@@ -755,9 +511,9 @@ final class InstallPackageHelper {
}
final int callingUid = Binder.getCallingUid();
- if (mPm.mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
!= PackageManager.PERMISSION_GRANTED
- && mPm.mContext.checkCallingOrSelfPermission(
+ && mContext.checkCallingOrSelfPermission(
android.Manifest.permission.INSTALL_EXISTING_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Neither user " + callingUid + " nor current process has "
@@ -810,7 +566,8 @@ final class InstallPackageHelper {
// upgrade app from instant to full; we don't allow app downgrade
installed = true;
}
- mPm.setInstantAppForUser(mInjector, pkgSetting, userId, instantApp, fullApp);
+ ScanPackageUtils.setInstantAppForUser(mPm.mInjector, pkgSetting, userId, instantApp,
+ fullApp);
}
if (installed) {
@@ -847,7 +604,7 @@ final class InstallPackageHelper {
mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
userId);
if (intentSender != null) {
- onRestoreComplete(res.mReturnCode, mPm.mContext, intentSender);
+ onRestoreComplete(res.mReturnCode, mContext, intentSender);
}
});
restoreAndPostInstall(userId, res, postInstallData);
@@ -933,9 +690,7 @@ final class InstallPackageHelper {
* Returns whether the restore successfully completed.
*/
private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- if (bm != null) {
+ if (mIBackupManager != null) {
// For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
// in the BackupManager. USER_ALL is used in compatibility tests.
if (userId == UserHandle.USER_ALL) {
@@ -946,8 +701,8 @@ final class InstallPackageHelper {
}
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
try {
- if (bm.isUserReadyForBackup(userId)) {
- bm.restoreAtInstallForUser(
+ if (mIBackupManager.isUserReadyForBackup(userId)) {
+ mIBackupManager.restoreAtInstallForUser(
userId, res.mPkg.getPackageName(), token);
} else {
Slog.w(TAG, "User " + userId + " is not ready. Restore at install "
@@ -973,8 +728,6 @@ final class InstallPackageHelper {
*/
private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res,
PostInstallData data) {
- RollbackManagerInternal rm = mInjector.getLocalService(RollbackManagerInternal.class);
-
final String packageName = res.mPkg.getPackageName();
final int[] allUsers = mPm.mUserManager.getUserIds();
final int[] installedUsers;
@@ -1000,8 +753,8 @@ final class InstallPackageHelper {
if (ps != null && doSnapshotOrRestore) {
final String seInfo = AndroidPackageUtils.getSeInfo(res.mPkg, ps);
- rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
- appId, ceDataInode, seInfo, token);
+ mRollbackManager.snapshotAndRestoreUserData(packageName,
+ UserHandle.toUserHandles(installedUsers), appId, ceDataInode, seInfo, token);
return true;
}
return false;
@@ -1093,7 +846,7 @@ final class InstallPackageHelper {
+ " got: " + apexes.length);
}
try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
- mPm.mApexManager.installPackage(apexes[0], packageParser);
+ mApexManager.installPackage(apexes[0], packageParser);
}
} catch (PackageManagerException e) {
request.mInstallResult.setError("APEX installation failed", e);
@@ -1170,7 +923,7 @@ final class InstallPackageHelper {
installResults.put(packageName, request.mInstallResult);
installArgs.put(packageName, request.mArgs);
try {
- final ScanResult result = mScanPackageHelper.scanPackageTracedLI(
+ final ScanResult result = scanPackageTracedLI(
prepareResult.mPackageToScan, prepareResult.mParseFlags,
prepareResult.mScanFlags, System.currentTimeMillis(),
request.mArgs.mUser, request.mArgs.mAbiOverride);
@@ -1183,6 +936,9 @@ final class InstallPackageHelper {
+ " in multi-package install request.");
return;
}
+ if (result.needsNewAppId()) {
+ request.mInstallResult.mRemovedInfo.mAppIdChanging = true;
+ }
createdAppId.put(packageName, optimisticallyRegisterAppId(result));
versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
@@ -1213,7 +969,7 @@ final class InstallPackageHelper {
Map<String, ReconciledPackage> reconciledPackages;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
- reconciledPackages = reconcilePackagesLocked(
+ reconciledPackages = ReconcilePackageUtils.reconcilePackages(
reconcileRequest, mPm.mSettings.getKeySetManagerService(),
mPm.mInjector);
} catch (ReconcileFailure e) {
@@ -1256,7 +1012,7 @@ final class InstallPackageHelper {
.buildVerificationRootHashString(baseCodePath, splitCodePaths);
VerificationUtils.broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_ALLOW, rootHashString,
- args.mDataLoaderType, args.getUser(), mPm.mContext);
+ args.mDataLoaderType, args.getUser(), mContext);
}
} else {
for (ScanResult result : preparedScans.values()) {
@@ -1472,9 +1228,11 @@ final class InstallPackageHelper {
} else {
try {
final boolean compareCompat =
- isCompatSignatureUpdateNeeded(parsedPackage);
+ ReconcilePackageUtils.isCompatSignatureUpdateNeeded(
+ mPm.getSettingsVersionForPackage(parsedPackage));
final boolean compareRecover =
- isRecoverSignatureUpdateNeeded(parsedPackage);
+ ReconcilePackageUtils.isRecoverSignatureUpdateNeeded(
+ mPm.getSettingsVersionForPackage(parsedPackage));
// We don't care about disabledPkgSetting on install for now.
final boolean compatMatch = verifySignatures(signatureCheckPs, null,
parsedPackage.getSigningDetails(), compareCompat, compareRecover,
@@ -1676,9 +1434,9 @@ final class InstallPackageHelper {
final String abiOverride = deriveAbiOverride(args.mAbiOverride);
boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
- derivedAbi = mPm.mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
+ derivedAbi = mPackageAbiHelper.derivePackageAbi(parsedPackage,
isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
- abiOverride, mPm.mAppLib32InstallDir);
+ abiOverride, ScanPackageUtils.getAppLib32InstallDir());
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
} catch (PackageManagerException pme) {
@@ -2376,8 +2134,8 @@ final class InstallPackageHelper {
// that can be used for all the packages.
final String codePath = ps.getPathString();
if (IncrementalManager.isIncrementalPath(codePath)
- && mPm.mIncrementalManager != null) {
- mPm.mIncrementalManager.registerLoadingProgressCallback(codePath,
+ && mIncrementalManager != null) {
+ mIncrementalManager.registerLoadingProgressCallback(codePath,
new IncrementalProgressListener(ps.getPackageName(), mPm));
}
@@ -2438,17 +2196,16 @@ final class InstallPackageHelper {
*/
private void executePostCommitSteps(CommitRequest commitRequest) {
final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
- final AppDataHelper appDataHelper = new AppDataHelper(mPm);
for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) {
final boolean instantApp = ((reconciledPkg.mScanResult.mRequest.mScanFlags
& SCAN_AS_INSTANT_APP) != 0);
final AndroidPackage pkg = reconciledPkg.mPkgSetting.getPkg();
final String packageName = pkg.getPackageName();
final String codePath = pkg.getPath();
- final boolean onIncremental = mPm.mIncrementalManager != null
+ final boolean onIncremental = mIncrementalManager != null
&& isIncrementalPath(codePath);
if (onIncremental) {
- IncrementalStorage storage = mPm.mIncrementalManager.openStorage(codePath);
+ IncrementalStorage storage = mIncrementalManager.openStorage(codePath);
if (storage == null) {
throw new IllegalArgumentException(
"Install: null storage for incremental package " + packageName);
@@ -2460,28 +2217,28 @@ final class InstallPackageHelper {
// Only set previousAppId if the app is migrating out of shared UID
previousAppId = reconciledPkg.mScanResult.mPreviousAppId;
}
- appDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
+ mAppDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
if (reconciledPkg.mPrepareResult.mClearCodeCache) {
- appDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
+ mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
if (reconciledPkg.mPrepareResult.mReplace) {
- mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
+ mDexManager.notifyPackageUpdated(pkg.getPackageName(),
pkg.getBaseApkPath(), pkg.getSplitCodePaths());
}
// Prepare the application profiles for the new code paths.
// This needs to be done before invoking dexopt so that any install-time profile
// can be used for optimizations.
- mPm.mArtManagerService.prepareAppProfiles(
+ mArtManagerService.prepareAppProfiles(
pkg,
mPm.resolveUserIds(reconciledPkg.mInstallArgs.mUser.getIdentifier()),
/* updateReferenceProfileContent= */ true);
// Compute the compilation reason from the installation scenario.
final int compilationReason =
- mPm.getDexManager().getCompilationReasonForInstallScenario(
+ mDexManager.getCompilationReasonForInstallScenario(
reconciledPkg.mInstallArgs.mInstallScenario);
// Construct the DexoptOptions early to see if we should skip running dexopt.
@@ -2529,7 +2286,7 @@ final class InstallPackageHelper {
// path moved to SCENARIO_FAST.
final boolean performDexopt =
(!instantApp || android.provider.Settings.Global.getInt(
- mPm.mContext.getContentResolver(),
+ mContext.getContentResolver(),
android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& !pkg.isDebuggable()
&& (!onIncremental)
@@ -2539,7 +2296,7 @@ final class InstallPackageHelper {
// Compile the layout resources.
if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
- mPm.mViewCompiler.compileLayouts(pkg);
+ mViewCompiler.compileLayouts(pkg);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -2564,10 +2321,10 @@ final class InstallPackageHelper {
realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
- mPm.mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
+ mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
null /* instructionSets */,
mPm.getOrCreateCompilerPackageStats(pkg),
- mPm.getDexManager().getPackageUseInfoOrDefault(packageName),
+ mDexManager.getPackageUseInfoOrDefault(packageName),
dexoptOptions);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -2580,7 +2337,8 @@ final class InstallPackageHelper {
notifyPackageChangeObserversOnUpdate(reconciledPkg);
}
- waitForNativeBinariesExtraction(incrementalStorages);
+ PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
+ incrementalStorages);
}
private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
@@ -2599,27 +2357,6 @@ final class InstallPackageHelper {
mPm.notifyPackageChangeObservers(pkgChangeEvent);
}
- private static void waitForNativeBinariesExtraction(
- ArraySet<IncrementalStorage> incrementalStorages) {
- if (incrementalStorages.isEmpty()) {
- return;
- }
- try {
- // Native library extraction may take very long time: each page could potentially
- // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds
- // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't
- // make much sense as blocking here doesn't lock up the framework, but only blocks
- // the installation session and the following ones.
- Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract");
- for (int i = 0; i < incrementalStorages.size(); ++i) {
- IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i);
- storage.waitForNativeBinariesExtraction();
- }
- } finally {
- Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract");
- }
- }
-
public int installLocationPolicy(PackageInfoLite pkgLite, int installFlags) {
String packageName = pkgLite.packageName;
int installLocation = pkgLite.installLocation;
@@ -2705,7 +2442,7 @@ final class InstallPackageHelper {
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
dataOwnerPkg.isDebuggable())) {
try {
- checkDowngrade(dataOwnerPkg, pkgLite);
+ PackageManagerServiceUtils.checkDowngrade(dataOwnerPkg, pkgLite);
} catch (PackageManagerException e) {
String errorMsg = "Downgrade detected: " + e.getMessage();
Slog.w(TAG, errorMsg);
@@ -2722,7 +2459,7 @@ final class InstallPackageHelper {
long requiredInstalledVersionCode, int installFlags) {
String packageName = pkgLite.packageName;
- final PackageInfo activePackage = mPm.mApexManager.getPackageInfo(packageName,
+ final PackageInfo activePackage = mApexManager.getPackageInfo(packageName,
ApexManager.MATCH_ACTIVE_PACKAGE);
if (activePackage == null) {
String errorMsg = "Attempting to install new APEX package " + packageName;
@@ -2755,41 +2492,6 @@ final class InstallPackageHelper {
return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
}
- /**
- * Check and throw if the given before/after packages would be considered a
- * downgrade.
- */
- private static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
- throws PackageManagerException {
- if (after.getLongVersionCode() < before.getLongVersionCode()) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update version code " + after.versionCode + " is older than current "
- + before.getLongVersionCode());
- } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
- if (after.baseRevisionCode < before.getBaseRevisionCode()) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update base revision code " + after.baseRevisionCode
- + " is older than current " + before.getBaseRevisionCode());
- }
-
- if (!ArrayUtils.isEmpty(after.splitNames)) {
- for (int i = 0; i < after.splitNames.length; i++) {
- final String splitName = after.splitNames[i];
- final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
- if (j != -1) {
- if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update split " + splitName + " revision code "
- + after.splitRevisionCodes[i]
- + " is older than current "
- + before.getSplitRevisionCodes()[j]);
- }
- }
- }
- }
- }
- }
-
int getUidForVerifier(VerifierInfo verifierInfo) {
synchronized (mPm.mLock) {
final AndroidPackage pkg = mPm.mPackages.get(verifierInfo.packageName);
@@ -2883,6 +2585,8 @@ final class InstallPackageHelper {
final int dataLoaderType = installArgs.mDataLoaderType;
final boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
+ final int previousAppId = (res.mRemovedInfo != null && res.mRemovedInfo.mAppIdChanging)
+ ? res.mRemovedInfo.mUid : Process.INVALID_UID;
final String packageName = res.mName;
final PackageStateInternal pkgSetting =
succeeded ? mPm.getPackageStateInternal(packageName) : null;
@@ -2979,9 +2683,12 @@ final class InstallPackageHelper {
dataLoaderType);
// Send added for users that don't see the package for the first time
- Bundle extras = new Bundle(1);
+ Bundle extras = new Bundle();
extras.putInt(Intent.EXTRA_UID, res.mUid);
- if (update) {
+ if (previousAppId != Process.INVALID_UID) {
+ extras.putBoolean(Intent.EXTRA_UID_CHANGING, true);
+ extras.putInt(Intent.EXTRA_PREVIOUS_UID, previousAppId);
+ } else if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
@@ -3024,22 +2731,27 @@ final class InstallPackageHelper {
// Send replaced for users that don't see the package for the first time
if (update) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
- null);
- if (installerPackageName != null) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
- }
- if (notifyVerifier) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
+ // Only send PACKAGE_REPLACED if appId has not changed
+ if (previousAppId == Process.INVALID_UID) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
+ null);
+ if (installerPackageName != null) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/,
+ null);
+ }
+ if (notifyVerifier) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/,
+ null);
+ }
}
mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null /*package*/, null /*extras*/, 0 /*flags*/,
@@ -3062,10 +2774,8 @@ final class InstallPackageHelper {
// Send broadcast package appeared if external for all users
if (res.mPkg.isExternalStorage()) {
if (!update) {
- final StorageManager storage = mPm.mInjector.getSystemService(
- StorageManager.class);
VolumeInfo volume =
- storage.findVolumeByUuid(
+ mStorageManager.findVolumeByUuid(
StorageManager.convert(
res.mPkg.getVolumeUuid()).toString());
int packageExternalStorageType =
@@ -3147,7 +2857,7 @@ final class InstallPackageHelper {
// There's a race currently where some install events may interleave with an
// uninstall. This can lead to package info being null (b/36642664).
if (info != null) {
- mPm.getDexManager().notifyPackageInstalled(info, userId);
+ mDexManager.notifyPackageInstalled(info, userId);
}
}
}
@@ -3175,7 +2885,7 @@ final class InstallPackageHelper {
* @return the current "allow unknown sources" setting
*/
private int getUnknownSourcesSettings() {
- return android.provider.Settings.Secure.getIntForUser(mPm.mContext.getContentResolver(),
+ return android.provider.Settings.Secure.getIntForUser(mContext.getContentResolver(),
android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
-1, UserHandle.USER_SYSTEM);
}
@@ -3282,7 +2992,8 @@ final class InstallPackageHelper {
installPackageFromSystemLIF(stubPkg.getPath(),
mPm.mUserManager.getUserIds() /*allUserHandles*/,
null /*origUserHandles*/,
- true /*writeSettings*/);
+ true /*writeSettings*/,
+ Process.INVALID_UID /*previousAppId*/);
} catch (PackageManagerException pme) {
// Serious WTF; we have to be able to install the stub
Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
@@ -3304,7 +3015,7 @@ final class InstallPackageHelper {
mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
+ mDexManager.notifyPackageUpdated(pkg.getPackageName(),
pkg.getBaseApkPath(), pkg.getSplitCodePaths());
}
return true;
@@ -3358,7 +3069,7 @@ final class InstallPackageHelper {
packageName);
int ret = PackageManagerServiceUtils.decompressFiles(codePath, dstCodePath, packageName);
if (ret == PackageManager.INSTALL_SUCCEEDED) {
- ret = extractNativeBinaries(dstCodePath, packageName);
+ ret = PackageManagerServiceUtils.extractNativeBinaries(dstCodePath, packageName);
}
if (ret == PackageManager.INSTALL_SUCCEEDED) {
// NOTE: During boot, we have to delay releasing cblocks for no other reason than
@@ -3371,7 +3082,7 @@ final class InstallPackageHelper {
}
mPm.mReleaseOnSystemReady.add(dstCodePath);
} else {
- final ContentResolver resolver = mPm.mContext.getContentResolver();
+ final ContentResolver resolver = mContext.getContentResolver();
F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
}
} else {
@@ -3385,22 +3096,6 @@ final class InstallPackageHelper {
return dstCodePath;
}
- private static int extractNativeBinaries(File dstCodePath, String packageName) {
- final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(dstCodePath);
- return NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
- null /*abiOverride*/, false /*isIncremental*/);
- } catch (IOException e) {
- logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
- + "; pkg: " + packageName);
- return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- } finally {
- IoUtils.closeQuietly(handle);
- }
- }
-
/**
* Tries to restore the disabled system package after an update has been deleted.
*/
@@ -3418,14 +3113,17 @@ final class InstallPackageHelper {
// Reinstate the old system package
mPm.mSettings.enableSystemPackageLPw(disabledPs.getPkg().getPackageName());
// Remove any native libraries from the upgraded package.
- removeNativeBinariesLI(deletedPs);
+ PackageManagerServiceUtils.removeNativeBinariesLI(deletedPs);
// Install the system package
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
try {
synchronized (mPm.mInstallLock) {
+ final int[] origUsers = outInfo == null ? null : outInfo.mOrigUsers;
+ final int previousAppId = disabledPs.getAppId() != deletedPs.getAppId()
+ ? deletedPs.getAppId() : Process.INVALID_UID;
installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
- outInfo == null ? null : outInfo.mOrigUsers, writeSettings);
+ origUsers, writeSettings, previousAppId);
}
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": "
@@ -3461,18 +3159,13 @@ final class InstallPackageHelper {
}
}
- private static void removeNativeBinariesLI(PackageSetting ps) {
- if (ps != null) {
- NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath());
- }
- }
-
/**
* Installs a package that's already on the system partition.
*/
@GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
private void installPackageFromSystemLIF(@NonNull String codePathString,
- @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings)
+ @NonNull int[] allUserHandles, @Nullable int[] origUserHandles,
+ boolean writeSettings, int previousAppId)
throws PackageManagerException {
final File codePath = new File(codePathString);
@ParsingPackageUtils.ParseFlags int parseFlags =
@@ -3496,11 +3189,12 @@ final class InstallPackageHelper {
mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
setPackageInstalledForSystemPackage(pkg, allUserHandles,
- origUserHandles, writeSettings);
+ origUserHandles, writeSettings, previousAppId);
}
private void setPackageInstalledForSystemPackage(@NonNull AndroidPackage pkg,
- @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings) {
+ @NonNull int[] allUserHandles, @Nullable int[] origUserHandles,
+ boolean writeSettings, int previousAppId) {
// writer
synchronized (mPm.mLock) {
PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -3534,8 +3228,7 @@ final class InstallPackageHelper {
// The method below will take care of removing obsolete permissions and granting
// install permissions.
- mPm.mPermissionManager.onPackageInstalled(pkg,
- Process.INVALID_UID /* previousAppId */,
+ mPm.mPermissionManager.onPackageInstalled(pkg, previousAppId,
PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
UserHandle.USER_ALL);
for (final int userId : allUserHandles) {
@@ -3737,7 +3430,7 @@ final class InstallPackageHelper {
}
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
- mPm.mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
+ mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
}
// Delete invalid userdata apps
@@ -3820,7 +3513,7 @@ final class InstallPackageHelper {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
- try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
+ try (PackageParser2 pp = mPm.mInjector.getScanningPackageParser()) {
parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -3853,7 +3546,7 @@ final class InstallPackageHelper {
@PackageManagerService.ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
- final Pair<ScanResult, Boolean> scanResultPair = mScanPackageHelper.scanSystemPackageLI(
+ final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI(
parsedPackage, parseFlags, scanFlags, currentTime, user);
final ScanResult scanResult = scanResultPair.first;
boolean shouldHideSystemApp = scanResultPair.second;
@@ -3863,7 +3556,7 @@ final class InstallPackageHelper {
try {
final String pkgName = scanResult.mPkgSetting.getPackageName();
final Map<String, ReconciledPackage> reconcileResult =
- reconcilePackagesLocked(
+ ReconcilePackageUtils.reconcilePackages(
new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
mPm.mSharedLibraries,
@@ -3875,7 +3568,7 @@ final class InstallPackageHelper {
Collections.singletonMap(pkgName,
mPm.getSharedLibLatestVersionSetting(
scanResult))),
- mPm.mSettings.getKeySetManagerService(), mInjector);
+ mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
appIdCreated = optimisticallyRegisterAppId(scanResult);
commitReconciledScanResultLocked(
reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
@@ -3893,10 +3586,10 @@ final class InstallPackageHelper {
mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
}
}
- if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
+ if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
if (scanResult.mPkgSetting != null && scanResult.mPkgSetting.isLoading()) {
// Continue monitoring loading progress of active incremental packages
- mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
+ mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
}
}
@@ -3934,5 +3627,743 @@ final class InstallPackageHelper {
}
}
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
+ try {
+ return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
+ cpuAbiOverride);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ private ScanRequest prepareInitialScanRequest(@NonNull ParsedPackage parsedPackage,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags,
+ @Nullable UserHandle user, String cpuAbiOverride)
+ throws PackageManagerException {
+ final AndroidPackage platformPackage;
+ final String realPkgName;
+ final PackageSetting disabledPkgSetting;
+ final PackageSetting installedPkgSetting;
+ final PackageSetting originalPkgSetting;
+ final SharedUserSetting sharedUserSetting;
+
+ synchronized (mPm.mLock) {
+ platformPackage = mPm.getPlatformPackage();
+ final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
+ AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
+ realPkgName = ScanPackageUtils.getRealPackageName(parsedPackage, renamedPkgName);
+ if (realPkgName != null) {
+ ScanPackageUtils.ensurePackageRenamed(parsedPackage, renamedPkgName);
+ }
+ originalPkgSetting = getOriginalPackageLocked(parsedPackage, renamedPkgName);
+ installedPkgSetting = mPm.mSettings.getPackageLPr(parsedPackage.getPackageName());
+ if (mPm.mTransferredPackages.contains(parsedPackage.getPackageName())) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " was transferred to another, but its .apk remains");
+ }
+ disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(
+ parsedPackage.getPackageName());
+ sharedUserSetting = (parsedPackage.getSharedUserId() != null)
+ ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
+ 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
+ : null;
+ if (DEBUG_PACKAGE_SCANNING
+ && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
+ && sharedUserSetting != null) {
+ Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ + " (uid=" + sharedUserSetting.userId + "):"
+ + " packages=" + sharedUserSetting.packages);
+ }
+ }
+
+ final boolean isPlatformPackage = platformPackage != null
+ && platformPackage.getPackageName().equals(parsedPackage.getPackageName());
+
+ return new ScanRequest(parsedPackage, sharedUserSetting,
+ installedPkgSetting == null ? null : installedPkgSetting.getPkg() /* oldPkg */,
+ installedPkgSetting /* packageSetting */,
+ disabledPkgSetting /* disabledPackageSetting */,
+ originalPkgSetting /* originalPkgSetting */,
+ realPkgName, parseFlags, scanFlags, isPlatformPackage, user, cpuAbiOverride);
+ }
+
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
+
+ final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage, parseFlags,
+ scanFlags, user, cpuAbiOverride);
+ final PackageSetting installedPkgSetting = initialScanRequest.mPkgSetting;
+ final PackageSetting disabledPkgSetting = initialScanRequest.mDisabledPkgSetting;
+
+ boolean isUpdatedSystemApp;
+ if (installedPkgSetting != null) {
+ isUpdatedSystemApp = installedPkgSetting.getPkgState().isUpdatedSystemApp();
+ } else {
+ isUpdatedSystemApp = disabledPkgSetting != null;
+ }
+
+ final int newScanFlags = adjustScanFlags(scanFlags, installedPkgSetting, disabledPkgSetting,
+ user, parsedPackage);
+ ScanPackageUtils.applyPolicy(parsedPackage, newScanFlags,
+ mPm.getPlatformPackage(), isUpdatedSystemApp);
+
+ synchronized (mPm.mLock) {
+ assertPackageIsValid(parsedPackage, parseFlags, newScanFlags);
+ final ScanRequest request = new ScanRequest(parsedPackage,
+ initialScanRequest.mSharedUserSetting,
+ initialScanRequest.mOldPkg, installedPkgSetting, disabledPkgSetting,
+ initialScanRequest.mOriginalPkgSetting, initialScanRequest.mRealPkgName,
+ parseFlags, scanFlags, initialScanRequest.mIsPlatformPackage, user,
+ cpuAbiOverride);
+ return ScanPackageUtils.scanPackageOnlyLI(request, mPm.mInjector, mPm.mFactoryTest,
+ currentTime);
+ }
+ }
+
+ private Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user) throws PackageManagerException {
+ final boolean scanSystemPartition =
+ (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
+ final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage, parseFlags,
+ scanFlags, user, null);
+ final PackageSetting installedPkgSetting = initialScanRequest.mPkgSetting;
+ final PackageSetting originalPkgSetting = initialScanRequest.mOriginalPkgSetting;
+ final PackageSetting pkgSetting =
+ originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
+ final boolean pkgAlreadyExists = pkgSetting != null;
+ final String disabledPkgName = pkgAlreadyExists
+ ? pkgSetting.getPackageName() : parsedPackage.getPackageName();
+ final boolean isSystemPkgUpdated;
+ final boolean isUpgrade;
+ synchronized (mPm.mLock) {
+ isUpgrade = mPm.isDeviceUpgrading();
+ if (scanSystemPartition && !pkgAlreadyExists
+ && mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
+ // The updated-package data for /system apk remains inconsistently
+ // after the package data for /data apk is lost accidentally.
+ // To recover it, enable /system apk and install it as non-updated system app.
+ Slog.w(TAG, "Inconsistent package setting of updated system app for "
+ + disabledPkgName + ". To recover it, enable the system app "
+ + "and install it as non-updated system app.");
+ mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
+ }
+ final PackageSetting disabledPkgSetting =
+ mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
+ isSystemPkgUpdated = disabledPkgSetting != null;
+
+ if (DEBUG_INSTALL && isSystemPkgUpdated) {
+ Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
+ }
+
+ if (scanSystemPartition && isSystemPkgUpdated) {
+ // we're updating the disabled package, so, scan it as the package setting
+ final ScanRequest request = new ScanRequest(parsedPackage,
+ initialScanRequest.mSharedUserSetting,
+ null, disabledPkgSetting /* pkgSetting */,
+ null /* disabledPkgSetting */, null /* originalPkgSetting */,
+ null, parseFlags, scanFlags,
+ initialScanRequest.mIsPlatformPackage, user, null);
+ ScanPackageUtils.applyPolicy(parsedPackage, scanFlags,
+ mPm.getPlatformPackage(), true);
+ final ScanResult scanResult =
+ ScanPackageUtils.scanPackageOnlyLI(request, mPm.mInjector,
+ mPm.mFactoryTest, -1L);
+ if (scanResult.mExistingSettingCopied
+ && scanResult.mRequest.mPkgSetting != null) {
+ scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
+ }
+ }
+ } // End of mLock
+
+ final boolean newPkgChangedPaths = pkgAlreadyExists
+ && !pkgSetting.getPathString().equals(parsedPackage.getPath());
+ final boolean newPkgVersionGreater = pkgAlreadyExists
+ && parsedPackage.getLongVersionCode() > pkgSetting.getVersionCode();
+ final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
+ && newPkgChangedPaths && newPkgVersionGreater;
+ if (isSystemPkgBetter) {
+ // The version of the application on /system is greater than the version on
+ // /data. Switch back to the application on /system.
+ // It's safe to assume the application on /system will correctly scan. If not,
+ // there won't be a working copy of the application.
+ synchronized (mPm.mLock) {
+ // just remove the loaded entries from package lists
+ mPm.mPackages.remove(pkgSetting.getPackageName());
+ }
+ logCriticalInfo(Log.WARN,
+ "System package updated;"
+ + " name: " + pkgSetting.getPackageName()
+ + "; " + pkgSetting.getVersionCode() + " --> "
+ + parsedPackage.getLongVersionCode()
+ + "; " + pkgSetting.getPathString()
+ + " --> " + parsedPackage.getPath());
+
+ final InstallArgs args = new FileInstallArgs(
+ pkgSetting.getPathString(), getAppDexInstructionSets(
+ pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()), mPm);
+ args.cleanUpResourcesLI();
+ synchronized (mPm.mLock) {
+ mPm.mSettings.enableSystemPackageLPw(pkgSetting.getPackageName());
+ }
+ }
+
+ // The version of the application on the /system partition is less than or
+ // equal to the version on the /data partition. Throw an exception and use
+ // the application already installed on the /data partition.
+ if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
+ // In the case of a skipped package, commitReconciledScanResultLocked is not called to
+ // add the object to the "live" data structures, so this is the final mutation step
+ // for the package. Which means it needs to be finalized here to cache derived fields.
+ // This is relevant for cases where the disabled system package is used for flags or
+ // other metadata.
+ parsedPackage.hideAsFinal();
+ throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+ + " at " + parsedPackage.getPath() + " ignored: updated version "
+ + (pkgAlreadyExists ? String.valueOf(pkgSetting.getVersionCode()) : "unknown")
+ + " better than this " + parsedPackage.getLongVersionCode());
+ }
+
+ // Verify certificates against what was last scanned. Force re-collecting certificate in two
+ // special cases:
+ // 1) when scanning system, force re-collect only if system is upgrading.
+ // 2) when scanning /data, force re-collect only if the app is privileged (updated from
+ // preinstall, or treated as privileged, e.g. due to shared user ID).
+ final boolean forceCollect = scanSystemPartition ? isUpgrade
+ : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
+ if (DEBUG_VERIFY && forceCollect) {
+ Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
+ }
+
+ // Full APK verification can be skipped during certificate collection, only if the file is
+ // in verified partition, or can be verified on access (when apk verity is enabled). In both
+ // cases, only data in Signing Block is verified instead of the whole file.
+ final boolean skipVerify = scanSystemPartition
+ || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
+ ScanPackageUtils.collectCertificatesLI(pkgSetting, parsedPackage,
+ mPm.getSettingsVersionForPackage(parsedPackage), forceCollect, skipVerify,
+ mPm.isPreNMR1Upgrade());
+
+ // Reset profile if the application version is changed
+ maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
+
+ /*
+ * A new system app appeared, but we already had a non-system one of the
+ * same name installed earlier.
+ */
+ boolean shouldHideSystemApp = false;
+ // A new application appeared on /system, but, we already have a copy of
+ // the application installed on /data.
+ if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
+ && !pkgSetting.isSystem()) {
+
+ if (!parsedPackage.getSigningDetails()
+ .checkCapability(pkgSetting.getSigningDetails(),
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
+ && !pkgSetting.getSigningDetails().checkCapability(
+ parsedPackage.getSigningDetails(),
+ SigningDetails.CertCapabilities.ROLLBACK)) {
+ logCriticalInfo(Log.WARN,
+ "System package signature mismatch;"
+ + " name: " + pkgSetting.getPackageName());
+ try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
+ parsedPackage.getPackageName(),
+ "scanPackageInternalLI")) {
+ DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
+ deletePackageHelper.deletePackageLIF(parsedPackage.getPackageName(), null, true,
+ mPm.mUserManager.getUserIds(), 0, null, false);
+ }
+ } else if (newPkgVersionGreater) {
+ // The application on /system is newer than the application on /data.
+ // Simply remove the application on /data [keeping application data]
+ // and replace it with the version on /system.
+ logCriticalInfo(Log.WARN,
+ "System package enabled;"
+ + " name: " + pkgSetting.getPackageName()
+ + "; " + pkgSetting.getVersionCode() + " --> "
+ + parsedPackage.getLongVersionCode()
+ + "; " + pkgSetting.getPathString() + " --> "
+ + parsedPackage.getPath());
+ InstallArgs args = new FileInstallArgs(
+ pkgSetting.getPathString(), getAppDexInstructionSets(
+ pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()),
+ mPm);
+ synchronized (mPm.mInstallLock) {
+ args.cleanUpResourcesLI();
+ }
+ } else {
+ // The application on /system is older than the application on /data. Hide
+ // the application on /system and the version on /data will be scanned later
+ // and re-added like an update.
+ shouldHideSystemApp = true;
+ logCriticalInfo(Log.INFO,
+ "System package disabled;"
+ + " name: " + pkgSetting.getPackageName()
+ + "; old: " + pkgSetting.getPathString() + " @ "
+ + pkgSetting.getVersionCode()
+ + "; new: " + parsedPackage.getPath() + " @ "
+ + parsedPackage.getPath());
+ }
+ }
+
+ final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags,
+ scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
+ return new Pair<>(scanResult, shouldHideSystemApp);
+ }
+
+ /**
+ * Returns if forced apk verification can be skipped for the whole package, including splits.
+ */
+ private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
+ final String packageName = pkg.getPackageName();
+ if (!canSkipForcedApkVerification(packageName, pkg.getBaseApkPath())) {
+ return false;
+ }
+ // TODO: Allow base and splits to be verified individually.
+ String[] splitCodePaths = pkg.getSplitCodePaths();
+ if (!ArrayUtils.isEmpty(splitCodePaths)) {
+ for (int i = 0; i < splitCodePaths.length; i++) {
+ if (!canSkipForcedApkVerification(packageName, splitCodePaths[i])) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
+ * whether the apk contains signed root hash. Note that the signer's certificate still needs to
+ * match one in a trusted source, and should be done separately.
+ */
+ private boolean canSkipForcedApkVerification(String packageName, String apkPath) {
+ if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
+ return VerityUtils.hasFsverity(apkPath);
+ }
+
+ try {
+ final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
+ if (rootHashObserved == null) {
+ return false; // APK does not contain Merkle tree root hash.
+ }
+ synchronized (mPm.mInstallLock) {
+ // Returns whether the observed root hash matches what kernel has.
+ mPm.mInstaller.assertFsverityRootHashMatches(packageName, apkPath,
+ rootHashObserved);
+ return true;
+ }
+ } catch (Installer.InstallerException | IOException | DigestException
+ | NoSuchAlgorithmException e) {
+ Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
+ }
+ return false;
+ }
+
+ /**
+ * Clear the package profile if this was an upgrade and the package
+ * version was updated.
+ */
+ private void maybeClearProfilesForUpgradesLI(
+ @Nullable PackageSetting originalPkgSetting,
+ @NonNull AndroidPackage pkg) {
+ if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) {
+ return;
+ }
+ if (originalPkgSetting.getVersionCode() == pkg.getLongVersionCode()) {
+ return;
+ }
+
+ mAppDataHelper.clearAppProfilesLIF(pkg);
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, originalPkgSetting.getPackageName()
+ + " clear profile due to version change "
+ + originalPkgSetting.getVersionCode() + " != "
+ + pkg.getLongVersionCode());
+ }
+ }
+
+ /**
+ * Returns the original package setting.
+ * <p>A package can migrate its name during an update. In this scenario, a package
+ * designates a set of names that it considers as one of its original names.
+ * <p>An original package must be signed identically and it must have the same
+ * shared user [if any].
+ */
+ @GuardedBy("mPm.mLock")
+ @Nullable
+ private PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ if (ScanPackageUtils.isPackageRenamed(pkg, renamedPkgName)) {
+ return null;
+ }
+ for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
+ final PackageSetting originalPs =
+ mPm.mSettings.getPackageLPr(pkg.getOriginalPackages().get(i));
+ if (originalPs != null) {
+ // the package is already installed under its original name...
+ // but, should we use it?
+ if (!verifyPackageUpdateLPr(originalPs, pkg)) {
+ // the new package is incompatible with the original
+ continue;
+ } else if (originalPs.getSharedUser() != null) {
+ if (!originalPs.getSharedUser().name.equals(pkg.getSharedUserId())) {
+ // the shared user id is incompatible with the original
+ Slog.w(TAG, "Unable to migrate data from " + originalPs.getPackageName()
+ + " to " + pkg.getPackageName() + ": old uid "
+ + originalPs.getSharedUser().name
+ + " differs from " + pkg.getSharedUserId());
+ continue;
+ }
+ // TODO: Add case when shared user id is added [b/28144775]
+ } else {
+ if (DEBUG_UPGRADE) {
+ Log.v(TAG, "Renaming new package "
+ + pkg.getPackageName() + " to old name "
+ + originalPs.getPackageName());
+ }
+ }
+ return originalPs;
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy("mPm.mLock")
+ private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
+ if ((oldPkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
+ + " to " + newPkg.getPackageName()
+ + ": old package not in system partition");
+ return false;
+ } else if (mPm.mPackages.get(oldPkg.getPackageName()) != null) {
+ Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
+ + " to " + newPkg.getPackageName()
+ + ": old package still exists");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Asserts the parsed package is valid according to the given policy. If the
+ * package is invalid, for whatever reason, throws {@link PackageManagerException}.
+ * <p>
+ * Implementation detail: This method must NOT have any side effects. It would
+ * ideally be static, but, it requires locks to read system state.
+ *
+ * @throws PackageManagerException If the package fails any of the validation checks
+ */
+ private void assertPackageIsValid(AndroidPackage pkg,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ final @PackageManagerService.ScanFlags int scanFlags)
+ throws PackageManagerException {
+ if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
+ ScanPackageUtils.assertCodePolicy(pkg);
+ }
+
+ if (pkg.getPath() == null) {
+ // Bail out. The resource and code paths haven't been set.
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Code and resource paths haven't been set correctly");
+ }
+
+ // Check that there is an APEX package with the same name only during install/first boot
+ // after OTA.
+ final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
+ final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+ if ((isUserInstall || isFirstBootOrUpgrade)
+ && mApexManager.isApexPackage(pkg.getPackageName())) {
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ pkg.getPackageName()
+ + " is an APEX package and can't be installed as an APK.");
+ }
+
+ // Make sure we're not adding any bogus keyset info
+ final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
+ ksms.assertScannedPackageValid(pkg);
+
+ synchronized (mPm.mLock) {
+ // The special "android" package can only be defined once
+ if (pkg.getPackageName().equals("android")) {
+ if (mPm.getCoreAndroidApplication() != null) {
+ Slog.w(TAG, "*************************************************");
+ Slog.w(TAG, "Core android package being redefined. Skipping.");
+ Slog.w(TAG, " codePath=" + pkg.getPath());
+ Slog.w(TAG, "*************************************************");
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Core android package being redefined. Skipping.");
+ }
+ }
+
+ // A package name must be unique; don't allow duplicates
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0
+ && mPm.mPackages.containsKey(pkg.getPackageName())) {
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Application package " + pkg.getPackageName()
+ + " already installed. Skipping duplicate.");
+ }
+
+ if (pkg.isStaticSharedLibrary()) {
+ // Static libs have a synthetic package name containing the version
+ // but we still want the base name to be unique.
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0
+ && mPm.mPackages.containsKey(pkg.getManifestPackageName())) {
+ throw new PackageManagerException(
+ "Duplicate static shared lib provider package");
+ }
+ ScanPackageUtils.assertStaticSharedLibraryIsValid(pkg, scanFlags);
+ assertStaticSharedLibraryVersionCodeIsValid(pkg);
+ }
+
+ // If we're only installing presumed-existing packages, require that the
+ // scanned APK is both already known and at the path previously established
+ // for it. Previously unknown packages we pick up normally, but if we have an
+ // a priori expectation about this package's install presence, enforce it.
+ // With a singular exception for new system packages. When an OTA contains
+ // a new system package, we allow the codepath to change from a system location
+ // to the user-installed location. If we don't allow this change, any newer,
+ // user-installed version of the application will be ignored.
+ if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
+ if (mPm.isExpectingBetter(pkg.getPackageName())) {
+ Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
+ + pkg.getPackageName());
+ } else {
+ PackageSetting known = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+ if (known != null) {
+ if (DEBUG_PACKAGE_SCANNING) {
+ Log.d(TAG, "Examining " + pkg.getPath()
+ + " and requiring known path " + known.getPathString());
+ }
+ if (!pkg.getPath().equals(known.getPathString())) {
+ throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
+ "Application package " + pkg.getPackageName()
+ + " found at " + pkg.getPath()
+ + " but expected at " + known.getPathString()
+ + "; ignoring.");
+ }
+ } else {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Application package " + pkg.getPackageName()
+ + " not found; ignoring.");
+ }
+ }
+ }
+
+ // Verify that this new package doesn't have any content providers
+ // that conflict with existing packages. Only do this if the
+ // package isn't already installed, since we don't want to break
+ // things that are installed.
+ if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
+ mPm.mComponentResolver.assertProvidersNotDefined(pkg);
+ }
+
+ // If this package has defined explicit processes, then ensure that these are
+ // the only processes used by its components.
+ ScanPackageUtils.assertProcessesAreValid(pkg);
+
+ // Verify that packages sharing a user with a privileged app are marked as privileged.
+ assertPackageWithSharedUserIdIsPrivileged(pkg);
+
+ // Apply policies specific for runtime resource overlays (RROs).
+ if (pkg.getOverlayTarget() != null) {
+ assertOverlayIsValid(pkg, parseFlags, scanFlags);
+ }
+
+ // If the package is not on a system partition ensure it is signed with at least the
+ // minimum signature scheme version required for its target SDK.
+ ScanPackageUtils.assertMinSignatureSchemeIsValid(pkg, parseFlags);
+ }
+ }
+
+ private void assertStaticSharedLibraryVersionCodeIsValid(AndroidPackage pkg)
+ throws PackageManagerException {
+ // The version codes must be ordered as lib versions
+ long minVersionCode = Long.MIN_VALUE;
+ long maxVersionCode = Long.MAX_VALUE;
+
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mPm.mSharedLibraries.get(
+ pkg.getStaticSharedLibName());
+ if (versionedLib != null) {
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ SharedLibraryInfo libInfo = versionedLib.valueAt(i);
+ final long libVersionCode = libInfo.getDeclaringPackage()
+ .getLongVersionCode();
+ if (libInfo.getLongVersion() < pkg.getStaticSharedLibVersion()) {
+ minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
+ } else if (libInfo.getLongVersion()
+ > pkg.getStaticSharedLibVersion()) {
+ maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
+ } else {
+ minVersionCode = maxVersionCode = libVersionCode;
+ break;
+ }
+ }
+ }
+ if (pkg.getLongVersionCode() < minVersionCode
+ || pkg.getLongVersionCode() > maxVersionCode) {
+ throw new PackageManagerException("Static shared"
+ + " lib version codes must be ordered as lib versions");
+ }
+ }
+
+ private void assertOverlayIsValid(AndroidPackage pkg,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags) throws PackageManagerException {
+ // System overlays have some restrictions on their use of the 'static' state.
+ if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
+ // We are scanning a system overlay. This can be the first scan of the
+ // system/vendor/oem partition, or an update to the system overlay.
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ // This must be an update to a system overlay. Immutable overlays cannot be
+ // upgraded.
+ if (!mPm.isOverlayMutable(pkg.getPackageName())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName()
+ + " is static and cannot be upgraded.");
+ }
+ } else {
+ if ((scanFlags & SCAN_AS_VENDOR) != 0) {
+ if (pkg.getTargetSdkVersion() < ScanPackageUtils.getVendorPartitionVersion()) {
+ Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ + " targets an SDK below the required SDK level of vendor"
+ + " overlays ("
+ + ScanPackageUtils.getVendorPartitionVersion()
+ + ")."
+ + " This will become an install error in a future release");
+ }
+ } else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
+ Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ + " targets an SDK below the required SDK level of system"
+ + " overlays (" + Build.VERSION.SDK_INT + ")."
+ + " This will become an install error in a future release");
+ }
+ }
+ } else {
+ // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
+ // signed with the platform certificate. Check this in increasing order of
+ // computational cost.
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) {
+ final PackageSetting platformPkgSetting =
+ mPm.mSettings.getPackageLPr("android");
+ if (!comparePackageSignatures(platformPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName()
+ + " must target Q or later, "
+ + "or be signed with the platform certificate");
+ }
+ }
+
+ // A non-preloaded overlay package, without <overlay android:targetName>, will
+ // only be used if it is signed with the same certificate as its target OR if
+ // it is signed with the same certificate as a reference package declared
+ // in 'overlay-config-signature' tag of SystemConfig.
+ // If the target is already installed or 'overlay-config-signature' tag in
+ // SystemConfig is set, check this here to augment the last line of defense
+ // which is OMS.
+ if (pkg.getOverlayTargetOverlayableName() == null) {
+ final PackageSetting targetPkgSetting =
+ mPm.mSettings.getPackageLPr(pkg.getOverlayTarget());
+ if (targetPkgSetting != null) {
+ if (!comparePackageSignatures(targetPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ // check reference signature
+ if (mPm.mOverlayConfigSignaturePackage == null) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName() + " and target "
+ + pkg.getOverlayTarget() + " signed with"
+ + " different certificates, and the overlay lacks"
+ + " <overlay android:targetName>");
+ }
+ final PackageSetting refPkgSetting =
+ mPm.mSettings.getPackageLPr(
+ mPm.mOverlayConfigSignaturePackage);
+ if (!comparePackageSignatures(refPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName() + " signed with a different "
+ + "certificate than both the reference package and "
+ + "target " + pkg.getOverlayTarget() + ", and the "
+ + "overlay lacks <overlay android:targetName>");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void assertPackageWithSharedUserIdIsPrivileged(AndroidPackage pkg)
+ throws PackageManagerException {
+ if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
+ SharedUserSetting sharedUserSetting = null;
+ try {
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(),
+ 0, 0, false);
+ } catch (PackageManagerException ignore) {
+ }
+ if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
+ // Exempt SharedUsers signed with the platform key.
+ PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
+ if (!comparePackageSignatures(platformPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Apps that share a user with a "
+ + "privileged app must themselves be marked as privileged. "
+ + pkg.getPackageName() + " shares privileged user "
+ + pkg.getSharedUserId() + ".");
+ }
+ }
+ }
+ }
+
+ private @PackageManagerService.ScanFlags int adjustScanFlags(
+ @PackageManagerService.ScanFlags int scanFlags,
+ PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
+ AndroidPackage pkg) {
+ scanFlags = ScanPackageUtils.adjustScanFlagsWithPackageSetting(scanFlags, pkgSetting,
+ disabledPkgSetting, user);
+
+ // Exception for privileged apps that share a user with a priv-app.
+ final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
+ && ScanPackageUtils.getVendorPartitionVersion() < 28;
+ if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
+ && !pkg.isPrivileged()
+ && (pkg.getSharedUserId() != null)
+ && !skipVendorPrivilegeScan) {
+ SharedUserSetting sharedUserSetting = null;
+ synchronized (mPm.mLock) {
+ try {
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0,
+ 0, false);
+ } catch (PackageManagerException ignore) {
+ }
+ if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
+ // Exempt SharedUsers signed with the platform key.
+ // TODO(b/72378145) Fix this exemption. Force signature apps
+ // to allowlist their privileged permissions just like other
+ // priv-apps.
+ PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
+ if ((compareSignatures(
+ platformPkgSetting.getSigningDetails().getSignatures(),
+ pkg.getSigningDetails().getSignatures())
+ != PackageManager.SIGNATURE_MATCH)) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ }
+ }
+ }
+
+ return scanFlags;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8f6ac0744574..6f8703bf5a5d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -57,6 +57,7 @@ import android.app.ApplicationPackageManager;
import android.app.IActivityManager;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
+import android.app.backup.IBackupManager;
import android.app.role.RoleManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -582,13 +583,6 @@ public class PackageManagerService extends IPackageManager.Stub
/** Directory where installed applications are stored */
private final File mAppInstallDir;
- /** Directory where installed application's 32-bit native libraries are copied. */
- @VisibleForTesting
- final File mAppLib32InstallDir;
-
- static File getAppLib32InstallDir() {
- return new File(Environment.getDataDirectory(), "app-lib");
- }
// ----------------------------------------------------------------
@@ -975,6 +969,7 @@ public class PackageManagerService extends IPackageManager.Stub
private final DeletePackageHelper mDeletePackageHelper;
private final InitAndSystemPackageHelper mInitAndSystemPackageHelper;
private final AppDataHelper mAppDataHelper;
+ private final InstallPackageHelper mInstallPackageHelper;
private final PreferredActivityHelper mPreferredActivityHelper;
private final ResolveIntentHelper mResolveIntentHelper;
private final DexOptHelper mDexOptHelper;
@@ -1525,7 +1520,9 @@ public class PackageManagerService extends IPackageManager.Stub
new DefaultSystemWrapper(),
LocalServices::getService,
context::getSystemService,
- (i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm));
+ (i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm),
+ (i, pm) -> IBackupManager.Stub.asInterface(ServiceManager.getService(
+ Context.BACKUP_SERVICE)));
if (Build.VERSION.SDK_INT <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
@@ -1697,7 +1694,6 @@ public class PackageManagerService extends IPackageManager.Stub
mEnableFreeCacheV2 = testParams.enableFreeCacheV2;
mSdkVersion = testParams.sdkVersion;
mAppInstallDir = testParams.appInstallDir;
- mAppLib32InstallDir = testParams.appLib32InstallDir;
mIsEngBuild = testParams.isEngBuild;
mIsUserDebugBuild = testParams.isUserDebugBuild;
mIncrementalVersion = testParams.incrementalVersion;
@@ -1705,6 +1701,7 @@ public class PackageManagerService extends IPackageManager.Stub
mBroadcastHelper = testParams.broadcastHelper;
mAppDataHelper = testParams.appDataHelper;
+ mInstallPackageHelper = testParams.installPackageHelper;
mRemovePackageHelper = testParams.removePackageHelper;
mInitAndSystemPackageHelper = testParams.initAndSystemPackageHelper;
mDeletePackageHelper = testParams.deletePackageHelper;
@@ -1837,7 +1834,6 @@ public class PackageManagerService extends IPackageManager.Stub
mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager, mPmInternal);
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
- mAppLib32InstallDir = getAppLib32InstallDir();
mDomainVerificationConnection = new DomainVerificationConnection(this);
mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
@@ -1845,6 +1841,7 @@ public class PackageManagerService extends IPackageManager.Stub
mBroadcastHelper = new BroadcastHelper(mInjector);
mAppDataHelper = new AppDataHelper(this);
+ mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this);
mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
@@ -2006,7 +2003,7 @@ public class PackageManagerService extends IPackageManager.Stub
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
final List<String> changedAbiCodePath =
- ScanPackageHelper.applyAdjustedAbiToSharedUser(
+ ScanPackageUtils.applyAdjustedAbiToSharedUser(
setting, null /*scannedPackage*/,
mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
setting.packages, null /*scannedPackage*/));
@@ -4698,35 +4695,10 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
int installReason, List<String> whiteListedPermissions) {
- final InstallPackageHelper installPackageHelper = new InstallPackageHelper(
- this, mAppDataHelper);
- return installPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
+ return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
installReason, whiteListedPermissions, null);
}
- static void setInstantAppForUser(PackageManagerServiceInjector injector,
- PackageSetting pkgSetting, int userId, boolean instantApp, boolean fullApp) {
- // no state specified; do nothing
- if (!instantApp && !fullApp) {
- return;
- }
- if (userId != UserHandle.USER_ALL) {
- if (instantApp && !pkgSetting.getInstantApp(userId)) {
- pkgSetting.setInstantApp(true /*instantApp*/, userId);
- } else if (fullApp && pkgSetting.getInstantApp(userId)) {
- pkgSetting.setInstantApp(false /*instantApp*/, userId);
- }
- } else {
- for (int currentUserId : injector.getUserManagerInternal().getUserIds()) {
- if (instantApp && !pkgSetting.getInstantApp(currentUserId)) {
- pkgSetting.setInstantApp(true /*instantApp*/, currentUserId);
- } else if (fullApp && pkgSetting.getInstantApp(currentUserId)) {
- pkgSetting.setInstantApp(false /*instantApp*/, currentUserId);
- }
- }
- }
- }
-
boolean isUserRestricted(int userId, String restrictionKey) {
Bundle restrictions = mUserManager.getUserRestrictions(userId);
if (restrictions.getBoolean(restrictionKey, false)) {
@@ -6733,8 +6705,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
- if (!new InstallPackageHelper(this).enableCompressedPackage(deletedPkg,
- pkgSetting)) {
+ if (!mInstallPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting)) {
Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
+ "commpressed package " + setting.getPackageName());
updateAllowed[i] = false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 97a09ffc810f..d14cc1f3ebd9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.app.ActivityManagerInternal;
+import android.app.backup.IBackupManager;
import android.content.ComponentName;
import android.content.Context;
import android.os.Handler;
@@ -135,6 +136,7 @@ public class PackageManagerServiceInjector {
mDomainVerificationManagerInternalProducer;
private final Singleton<Handler> mHandlerProducer;
private final Singleton<BackgroundDexOptService> mBackgroundDexOptService;
+ private final Singleton<IBackupManager> mIBackupManager;
PackageManagerServiceInjector(Context context, PackageManagerTracedLock lock,
Installer installer, Object installLock, PackageAbiHelper abiHelper,
@@ -170,7 +172,8 @@ public class PackageManagerServiceInjector {
SystemWrapper systemWrapper,
ServiceProducer getLocalServiceProducer,
ServiceProducer getSystemServiceProducer,
- Producer<BackgroundDexOptService> backgroundDexOptService) {
+ Producer<BackgroundDexOptService> backgroundDexOptService,
+ Producer<IBackupManager> iBackupManager) {
mContext = context;
mLock = lock;
mInstaller = installer;
@@ -220,6 +223,7 @@ public class PackageManagerServiceInjector {
domainVerificationManagerInternalProducer);
mHandlerProducer = new Singleton<>(handlerProducer);
mBackgroundDexOptService = new Singleton<>(backgroundDexOptService);
+ mIBackupManager = new Singleton<>(iBackupManager);
}
/**
@@ -384,6 +388,10 @@ public class PackageManagerServiceInjector {
return mBackgroundDexOptService.get(this, mPackageManager);
}
+ public IBackupManager getIBackupManager() {
+ return mIBackupManager.get(this, mPackageManager);
+ }
+
/** Provides an abstraction to static access to system state. */
public interface SystemWrapper {
void disablePackageCaches();
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 9327c5f10af7..a1acc388146e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -104,6 +104,7 @@ public final class PackageManagerServiceTestParams {
public final String incrementalVersion = Build.VERSION.INCREMENTAL;
public BroadcastHelper broadcastHelper;
public AppDataHelper appDataHelper;
+ public InstallPackageHelper installPackageHelper;
public RemovePackageHelper removePackageHelper;
public InitAndSystemPackageHelper initAndSystemPackageHelper;
public DeletePackageHelper deletePackageHelper;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 4082afd13089..898f67345031 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -18,9 +18,11 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
+import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING;
@@ -34,7 +36,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -60,6 +62,7 @@ import android.os.FileUtils;
import android.os.Process;
import android.os.SystemProperties;
import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalStorage;
import android.os.incremental.V4Signature;
import android.os.incremental.V4Signature.HashingInfo;
import android.os.storage.DiskInfo;
@@ -84,6 +87,7 @@ import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.HexDump;
import com.android.server.EventLogTags;
import com.android.server.IntentResolver;
+import com.android.server.Watchdog;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -141,7 +145,7 @@ public class PackageManagerServiceUtils {
* allow 3P apps to trigger internal-only functionality.
*/
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188;
/**
@@ -649,6 +653,58 @@ public class PackageManagerServiceUtils {
}
/**
+ * Extract native libraries to a target path
+ */
+ public static int extractNativeBinaries(File dstCodePath, String packageName) {
+ final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(dstCodePath);
+ return NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
+ null /*abiOverride*/, false /*isIncremental*/);
+ } catch (IOException e) {
+ logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
+ + "; pkg: " + packageName);
+ return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ } finally {
+ IoUtils.closeQuietly(handle);
+ }
+ }
+
+ /**
+ * Remove native libraries of a given package
+ */
+ public static void removeNativeBinariesLI(PackageSetting ps) {
+ if (ps != null) {
+ NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath());
+ }
+ }
+
+ /**
+ * Wait for native library extraction to be done in IncrementalService
+ */
+ public static void waitForNativeBinariesExtractionForIncremental(
+ ArraySet<IncrementalStorage> incrementalStorages) {
+ if (incrementalStorages.isEmpty()) {
+ return;
+ }
+ try {
+ // Native library extraction may take very long time: each page could potentially
+ // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds
+ // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't
+ // make much sense as blocking here doesn't lock up the framework, but only blocks
+ // the installation session and the following ones.
+ Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract");
+ for (int i = 0; i < incrementalStorages.size(); ++i) {
+ IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i);
+ storage.waitForNativeBinariesExtraction();
+ }
+ } finally {
+ Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract");
+ }
+ }
+
+ /**
* Decompress files stored in codePath to dstCodePath for a certain package.
*/
public static int decompressFiles(String codePath, File dstCodePath, String packageName) {
@@ -1280,4 +1336,39 @@ public class PackageManagerServiceUtils {
return cacheDir;
}
+
+ /**
+ * Check and throw if the given before/after packages would be considered a
+ * downgrade.
+ */
+ public static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
+ throws PackageManagerException {
+ if (after.getLongVersionCode() < before.getLongVersionCode()) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update version code " + after.versionCode + " is older than current "
+ + before.getLongVersionCode());
+ } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
+ if (after.baseRevisionCode < before.getBaseRevisionCode()) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update base revision code " + after.baseRevisionCode
+ + " is older than current " + before.getBaseRevisionCode());
+ }
+
+ if (!ArrayUtils.isEmpty(after.splitNames)) {
+ for (int i = 0; i < after.splitNames.length; i++) {
+ final String splitName = after.splitNames[i];
+ final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
+ if (j != -1) {
+ if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update split " + splitName + " revision code "
+ + after.splitRevisionCodes[i]
+ + " is older than current "
+ + before.getSplitRevisionCodes()[j]);
+ }
+ }
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index a60d2c85f542..48dc3cbf8793 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -49,6 +49,7 @@ final class PackageRemovedInfo {
boolean mDataRemoved;
boolean mRemovedForAllUsers;
boolean mIsStaticSharedLib;
+ boolean mAppIdChanging = false;
// a two dimensional array mapping userId to the set of appIds that can receive notice
// of package changes
SparseArray<int[]> mBroadcastAllowList;
@@ -64,33 +65,43 @@ final class PackageRemovedInfo {
sendPackageRemovedBroadcastInternal(killApp, removedBySystem);
}
- void sendSystemPackageUpdatedBroadcasts() {
+ void sendSystemPackageUpdatedBroadcasts(int newAppId) {
if (mIsRemovedPackageSystemUpdate) {
- sendSystemPackageUpdatedBroadcastsInternal();
+ sendSystemPackageUpdatedBroadcastsInternal(newAppId);
}
}
- private void sendSystemPackageUpdatedBroadcastsInternal() {
+ private void sendSystemPackageUpdatedBroadcastsInternal(int newAppId) {
Bundle extras = new Bundle(2);
- extras.putInt(Intent.EXTRA_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid);
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ extras.putInt(Intent.EXTRA_UID, newAppId);
+ // When appId changes, do not set the replacing extra
+ if (mAppIdChanging) {
+ extras.putBoolean(Intent.EXTRA_UID_CHANGING, true);
+ extras.putInt(Intent.EXTRA_PREVIOUS_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid);
+ } else {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ }
mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, mRemovedPackage, extras,
0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage,
- extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
- mRemovedPackage, null, null, null, null /* broadcastAllowList */,
- getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());
if (mInstallerPackageName != null) {
mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
mRemovedPackage, extras, 0 /*flags*/,
mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- mRemovedPackage, extras, 0 /*flags*/,
- mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
- null);
}
+ if (!mAppIdChanging) {
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage,
+ extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
+ if (mInstallerPackageName != null) {
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ mRemovedPackage, extras, 0 /*flags*/,
+ mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
+ null);
+ }
+ }
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
+ mRemovedPackage, null, null, null, null /* broadcastAllowList */,
+ getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());
}
private static @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
@@ -115,15 +126,20 @@ final class PackageRemovedInfo {
if (mIsStaticSharedLib) {
return;
}
- Bundle extras = new Bundle(2);
+ Bundle extras = new Bundle();
final int removedUid = mRemovedAppId >= 0 ? mRemovedAppId : mUid;
extras.putInt(Intent.EXTRA_UID, removedUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, mDataRemoved);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
- if (mIsUpdate || mIsRemovedPackageSystemUpdate) {
+
+ // When appId changes, do not set the replacing extra
+ if (mAppIdChanging) {
+ extras.putBoolean(Intent.EXTRA_UID_CHANGING, true);
+ } else if (mIsUpdate || mIsRemovedPackageSystemUpdate) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
+
extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, mRemovedForAllUsers);
if (mRemovedPackage != null) {
mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
@@ -146,9 +162,9 @@ final class PackageRemovedInfo {
}
}
if (mRemovedAppId >= 0) {
- // If a system app's updates are uninstalled the UID is not actually removed. Some
- // services need to know the package name affected.
- if (extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+ // If the package is not actually removed, some services need to know the
+ // package name affected.
+ if (mAppIdChanging || mIsUpdate || mIsRemovedPackageSystemUpdate) {
extras.putString(Intent.EXTRA_PACKAGE_NAME, mRemovedPackage);
}
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
new file mode 100644
index 000000000000..5a250047da8f
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+
+import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
+import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
+import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
+
+import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.os.SystemProperties;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.utils.WatchedLongSparseArray;
+
+import java.util.List;
+import java.util.Map;
+
+final class ReconcilePackageUtils {
+ public static Map<String, ReconciledPackage> reconcilePackages(
+ final ReconcileRequest request, KeySetManagerService ksms,
+ PackageManagerServiceInjector injector)
+ throws ReconcileFailure {
+ final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
+
+ final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
+
+ // make a copy of the existing set of packages so we can combine them with incoming packages
+ final ArrayMap<String, AndroidPackage> combinedPackages =
+ new ArrayMap<>(request.mAllPackages.size() + scannedPackages.size());
+
+ combinedPackages.putAll(request.mAllPackages);
+
+ final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
+ new ArrayMap<>();
+
+ for (String installPackageName : scannedPackages.keySet()) {
+ final ScanResult scanResult = scannedPackages.get(installPackageName);
+
+ // add / replace existing with incoming packages
+ combinedPackages.put(scanResult.mPkgSetting.getPackageName(),
+ scanResult.mRequest.mParsedPackage);
+
+ // in the first pass, we'll build up the set of incoming shared libraries
+ final List<SharedLibraryInfo> allowedSharedLibInfos =
+ SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
+ request.mSharedLibrarySource);
+ if (allowedSharedLibInfos != null) {
+ for (SharedLibraryInfo info : allowedSharedLibInfos) {
+ if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
+ incomingSharedLibraries, info)) {
+ throw new ReconcileFailure("Shared Library " + info.getName()
+ + " is being installed twice in this set!");
+ }
+ }
+ }
+
+ // the following may be null if we're just reconciling on boot (and not during install)
+ final InstallArgs installArgs = request.mInstallArgs.get(installPackageName);
+ final PackageInstalledInfo res = request.mInstallResults.get(installPackageName);
+ final PrepareResult prepareResult = request.mPreparedPackages.get(installPackageName);
+ final boolean isInstall = installArgs != null;
+ if (isInstall && (res == null || prepareResult == null)) {
+ throw new ReconcileFailure("Reconcile arguments are not balanced for "
+ + installPackageName + "!");
+ }
+
+ final DeletePackageAction deletePackageAction;
+ // we only want to try to delete for non system apps
+ if (isInstall && prepareResult.mReplace && !prepareResult.mSystem) {
+ final boolean killApp = (scanResult.mRequest.mScanFlags & SCAN_DONT_KILL_APP) == 0;
+ final int deleteFlags = PackageManager.DELETE_KEEP_DATA
+ | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
+ deletePackageAction = DeletePackageHelper.mayDeletePackageLocked(res.mRemovedInfo,
+ prepareResult.mOriginalPs, prepareResult.mDisabledPs,
+ deleteFlags, null /* all users */);
+ if (deletePackageAction == null) {
+ throw new ReconcileFailure(
+ PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
+ "May not delete " + installPackageName + " to replace");
+ }
+ } else {
+ deletePackageAction = null;
+ }
+
+ final int scanFlags = scanResult.mRequest.mScanFlags;
+ final int parseFlags = scanResult.mRequest.mParseFlags;
+ final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
+
+ final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting;
+ final PackageSetting lastStaticSharedLibSetting =
+ request.mLastStaticSharedLibSettings.get(installPackageName);
+ final PackageSetting signatureCheckPs =
+ (prepareResult != null && lastStaticSharedLibSetting != null)
+ ? lastStaticSharedLibSetting
+ : scanResult.mPkgSetting;
+ boolean removeAppKeySetData = false;
+ boolean sharedUserSignaturesChanged = false;
+ SigningDetails signingDetails = null;
+ if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+ if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
+ // We just determined the app is signed correctly, so bring
+ // over the latest parsed certs.
+ } else {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "Package " + parsedPackage.getPackageName()
+ + " upgrade keys do not match the previously installed"
+ + " version");
+ } else {
+ String msg = "System package " + parsedPackage.getPackageName()
+ + " signature changed; retaining data.";
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ }
+ }
+ signingDetails = parsedPackage.getSigningDetails();
+ } else {
+ try {
+ final Settings.VersionInfo versionInfo =
+ request.mVersionInfos.get(installPackageName);
+ final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
+ final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
+ final boolean isRollback = installArgs != null
+ && installArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ final boolean compatMatch = verifySignatures(signatureCheckPs,
+ disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
+ compareRecover, isRollback);
+ // The new KeySets will be re-added later in the scanning process.
+ if (compatMatch) {
+ removeAppKeySetData = true;
+ }
+ // We just determined the app is signed correctly, so bring
+ // over the latest parsed certs.
+ signingDetails = parsedPackage.getSigningDetails();
+
+ // if this is is a sharedUser, check to see if the new package is signed by a
+ // newer
+ // signing certificate than the existing one, and if so, copy over the new
+ // details
+ if (signatureCheckPs.getSharedUser() != null) {
+ // Attempt to merge the existing lineage for the shared SigningDetails with
+ // the lineage of the new package; if the shared SigningDetails are not
+ // returned this indicates the new package added new signers to the lineage
+ // and/or changed the capabilities of existing signers in the lineage.
+ SigningDetails sharedSigningDetails =
+ signatureCheckPs.getSharedUser().signatures.mSigningDetails;
+ SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
+ signingDetails);
+ if (mergedDetails != sharedSigningDetails) {
+ signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+ mergedDetails;
+ }
+ if (signatureCheckPs.getSharedUser().signaturesChanged == null) {
+ signatureCheckPs.getSharedUser().signaturesChanged = Boolean.FALSE;
+ }
+ }
+ } catch (PackageManagerException e) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ throw new ReconcileFailure(e);
+ }
+ signingDetails = parsedPackage.getSigningDetails();
+
+ // If the system app is part of a shared user we allow that shared user to
+ // change
+ // signatures as well as part of an OTA. We still need to verify that the
+ // signatures
+ // are consistent within the shared user for a given boot, so only allow
+ // updating
+ // the signatures on the first package scanned for the shared user (i.e. if the
+ // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
+ if (signatureCheckPs.getSharedUser() != null) {
+ final Signature[] sharedUserSignatures = signatureCheckPs.getSharedUser()
+ .signatures.mSigningDetails.getSignatures();
+ if (signatureCheckPs.getSharedUser().signaturesChanged != null
+ && compareSignatures(sharedUserSignatures,
+ parsedPackage.getSigningDetails().getSignatures())
+ != PackageManager.SIGNATURE_MATCH) {
+ if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
+ // Mismatched signatures is an error and silently skipping system
+ // packages will likely break the device in unforeseen ways.
+ // However, we allow the device to boot anyway because, prior to Q,
+ // vendors were not expecting the platform to crash in this
+ // situation.
+ // This WILL be a hard failure on any new API levels after Q.
+ throw new ReconcileFailure(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ "Signature mismatch for shared user: "
+ + scanResult.mPkgSetting.getSharedUser());
+ } else {
+ // Treat mismatched signatures on system packages using a shared
+ // UID as
+ // fatal for the system overall, rather than just failing to install
+ // whichever package happened to be scanned later.
+ throw new IllegalStateException(
+ "Signature mismatch on system package "
+ + parsedPackage.getPackageName()
+ + " for shared user "
+ + scanResult.mPkgSetting.getSharedUser());
+ }
+ }
+
+ sharedUserSignaturesChanged = true;
+ signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+ parsedPackage.getSigningDetails();
+ signatureCheckPs.getSharedUser().signaturesChanged = Boolean.TRUE;
+ }
+ // File a report about this.
+ String msg = "System package " + parsedPackage.getPackageName()
+ + " signature changed; retaining data.";
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ } catch (IllegalArgumentException e) {
+ // should never happen: certs matched when checking, but not when comparing
+ // old to new for sharedUser
+ throw new RuntimeException(
+ "Signing certificates comparison made on incomparable signing details"
+ + " but somehow passed verifySignatures!", e);
+ }
+ }
+
+ result.put(installPackageName,
+ new ReconciledPackage(request, installArgs, scanResult.mPkgSetting,
+ res, request.mPreparedPackages.get(installPackageName), scanResult,
+ deletePackageAction, allowedSharedLibInfos, signingDetails,
+ sharedUserSignaturesChanged, removeAppKeySetData));
+ }
+
+ for (String installPackageName : scannedPackages.keySet()) {
+ // Check all shared libraries and map to their actual file path.
+ // We only do this here for apps not on a system dir, because those
+ // are the only ones that can fail an install due to this. We
+ // will take care of the system apps by updating all of their
+ // library paths after the scan is done. Also during the initial
+ // scan don't update any libs as we do this wholesale after all
+ // apps are scanned to avoid dependency based scanning.
+ final ScanResult scanResult = scannedPackages.get(installPackageName);
+ if ((scanResult.mRequest.mScanFlags & SCAN_BOOTING) != 0
+ || (scanResult.mRequest.mParseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
+ != 0) {
+ continue;
+ }
+ try {
+ result.get(installPackageName).mCollectedSharedLibraryInfos =
+ SharedLibraryHelper.collectSharedLibraryInfos(
+ scanResult.mRequest.mParsedPackage,
+ combinedPackages, request.mSharedLibrarySource,
+ incomingSharedLibraries, injector.getCompatibility());
+
+ } catch (PackageManagerException e) {
+ throw new ReconcileFailure(e.error, e.getMessage());
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * If the database version for this type of package (internal storage or
+ * external storage) is less than the version where package signatures
+ * were updated, return true.
+ */
+ public static boolean isCompatSignatureUpdateNeeded(Settings.VersionInfo ver) {
+ return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_END_ENTITY;
+ }
+
+ public static boolean isRecoverSignatureUpdateNeeded(Settings.VersionInfo ver) {
+ return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 9f3d19005de6..19f180f1ce4c 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -125,16 +125,10 @@ public final class SELinuxMMAC {
}
// Vendor mac permissions.
- // The filename has been renamed from nonplat_mac_permissions to
- // vendor_mac_permissions. Either of them should exist.
final File vendorMacPermission = new File(
Environment.getVendorDirectory(), "/etc/selinux/vendor_mac_permissions.xml");
if (vendorMacPermission.exists()) {
sMacPermissions.add(vendorMacPermission);
- } else {
- // For backward compatibility.
- sMacPermissions.add(new File(Environment.getVendorDirectory(),
- "/etc/selinux/nonplat_mac_permissions.xml"));
}
// ODM mac permissions (optional).
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
deleted file mode 100644
index eafe0d98e505..000000000000
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ /dev/null
@@ -1,1716 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.PackageManagerService.DEBUG_ABI_SELECTION;
-import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
-import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
-import static com.android.server.pm.PackageManagerService.DEBUG_UPGRADE;
-import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
-import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
-import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
-import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
-import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
-import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
-import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
-import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
-import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_TIME;
-import static com.android.server.pm.PackageManagerService.TAG;
-import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
-import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
-import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
-import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
-import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
-import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.SharedLibraryInfo;
-import android.content.pm.SigningDetails;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.component.ComponentMutateUtils;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedProcess;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
-import android.os.Build;
-import android.os.Process;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Slog;
-import android.util.apk.ApkSignatureVerifier;
-import android.util.jar.StrictJarFile;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.security.VerityUtils;
-import com.android.internal.util.ArrayUtils;
-import com.android.server.SystemConfig;
-import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.utils.WatchedLongSparseArray;
-
-import dalvik.system.VMRuntime;
-
-import java.io.File;
-import java.io.IOException;
-import java.security.DigestException;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * Helper class that handles package scanning logic
- */
-final class ScanPackageHelper {
- final PackageManagerService mPm;
- final PackageManagerServiceInjector mInjector;
-
- // TODO(b/198166813): remove PMS dependency
- public ScanPackageHelper(PackageManagerService pm) {
- mPm = pm;
- mInjector = pm.mInjector;
- }
-
- ScanPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
- mPm = pm;
- mInjector = injector;
- }
-
- /**
- * Similar to the other scanPackageTracedLI but accepting a ParsedPackage instead of a File.
- */
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- public ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
- final @ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
- try {
- return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
- cpuAbiOverride);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- // TODO(b/199937291): scanPackageNewLI() and scanPackageOnlyLI() should be merged.
- // But, first, committing the results / removing app data needs to be moved up a level to the
- // callers of this method. Also, we need to solve the problem of potentially creating a new
- // shared user setting. That can probably be done later and patch things up after the fact.
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
- final @ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
-
- final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
- AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
- final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
- if (realPkgName != null) {
- ensurePackageRenamed(parsedPackage, renamedPkgName);
- }
- final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
- renamedPkgName);
- final PackageSetting pkgSetting =
- mPm.mSettings.getPackageLPr(parsedPackage.getPackageName());
- final PackageSetting disabledPkgSetting =
- mPm.mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());
-
- if (mPm.mTransferredPackages.contains(parsedPackage.getPackageName())) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " was transferred to another, but its .apk remains");
- }
-
- scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);
- synchronized (mPm.mLock) {
- boolean isUpdatedSystemApp;
- if (pkgSetting != null) {
- isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
- } else {
- isUpdatedSystemApp = disabledPkgSetting != null;
- }
- applyPolicy(parsedPackage, scanFlags, mPm.getPlatformPackage(), isUpdatedSystemApp);
- assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
-
- SharedUserSetting sharedUserSetting = null;
- if (parsedPackage.getSharedUserId() != null) {
- // SIDE EFFECTS; may potentially allocate a new shared user
- sharedUserSetting = mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
- 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
- Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
- + " (uid=" + sharedUserSetting.userId + "):"
- + " packages=" + sharedUserSetting.packages);
- }
- }
- }
- String platformPackageName = mPm.getPlatformPackage() == null
- ? null : mPm.getPlatformPackage().getPackageName();
- final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
- pkgSetting == null ? null : pkgSetting.getPkg(), pkgSetting, disabledPkgSetting,
- originalPkgSetting, realPkgName, parseFlags, scanFlags,
- Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,
- cpuAbiOverride);
- return scanPackageOnlyLI(request, mInjector, mPm.mFactoryTest, currentTime);
- }
- }
-
- /**
- * Just scans the package without any side effects.
- * <p>Not entirely true at the moment. There is still one side effect -- this
- * method potentially modifies a live {@link PackageSetting} object representing
- * the package being scanned. This will be resolved in the future.
- *
- * @param injector injector for acquiring dependencies
- * @param request Information about the package to be scanned
- * @param isUnderFactoryTest Whether or not the device is under factory test
- * @param currentTime The current time, in millis
- * @return The results of the scan
- */
- @GuardedBy("mPm.mInstallLock")
- @VisibleForTesting
- @NonNull
- public ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
- PackageManagerServiceInjector injector,
- boolean isUnderFactoryTest, long currentTime)
- throws PackageManagerException {
- final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
- ParsedPackage parsedPackage = request.mParsedPackage;
- PackageSetting pkgSetting = request.mPkgSetting;
- final PackageSetting disabledPkgSetting = request.mDisabledPkgSetting;
- final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
- final @ParsingPackageUtils.ParseFlags int parseFlags = request.mParseFlags;
- final @PackageManagerService.ScanFlags int scanFlags = request.mScanFlags;
- final String realPkgName = request.mRealPkgName;
- final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
- final UserHandle user = request.mUser;
- final boolean isPlatformPackage = request.mIsPlatformPackage;
-
- List<String> changedAbiCodePath = null;
-
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
- Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
- }
- }
-
- // Initialize package source and resource directories
- final File destCodeFile = new File(parsedPackage.getPath());
-
- // We keep references to the derived CPU Abis from settings in oder to reuse
- // them in the case where we're not upgrading or booting for the first time.
- String primaryCpuAbiFromSettings = null;
- String secondaryCpuAbiFromSettings = null;
- boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
- if (!needToDeriveAbi) {
- if (pkgSetting != null) {
- // TODO(b/154610922): if it is not first boot or upgrade, we should directly use
- // API info from existing package setting. However, stub packages currently do not
- // preserve ABI info, thus the special condition check here. Remove the special
- // check after we fix the stub generation.
- if (pkgSetting.getPkg() != null && pkgSetting.getPkg().isStub()) {
- needToDeriveAbi = true;
- } else {
- primaryCpuAbiFromSettings = pkgSetting.getPrimaryCpuAbi();
- secondaryCpuAbiFromSettings = pkgSetting.getSecondaryCpuAbi();
- }
- } else {
- // Re-scanning a system package after uninstalling updates; need to derive ABI
- needToDeriveAbi = true;
- }
- }
-
- int previousAppId = Process.INVALID_UID;
-
- if (pkgSetting != null && pkgSetting.getSharedUser() != sharedUserSetting) {
- if (pkgSetting.getSharedUser() != null && sharedUserSetting == null) {
- previousAppId = pkgSetting.getAppId();
- // Log that something is leaving shareduid and keep going
- Slog.i(TAG,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + pkgSetting.getSharedUser().name + " to " + "<nothing>.");
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + (pkgSetting.getSharedUser() != null
- ? pkgSetting.getSharedUser().name : "<nothing>")
- + " to "
- + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
- + "; replacing with new");
- pkgSetting = null;
- }
- }
-
- String[] usesSdkLibraries = null;
- if (!parsedPackage.getUsesSdkLibraries().isEmpty()) {
- usesSdkLibraries = new String[parsedPackage.getUsesSdkLibraries().size()];
- parsedPackage.getUsesSdkLibraries().toArray(usesSdkLibraries);
- }
-
- String[] usesStaticLibraries = null;
- if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
- usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
- parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
- }
-
- final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();
-
- // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
- // to avoid adding something that's unsupported due to lack of state, since it's called
- // with null.
- final boolean createNewPackage = (pkgSetting == null);
- if (createNewPackage) {
- final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
-
- // Flags contain system values stored in the server variant of AndroidPackage,
- // and so the server-side PackageInfoUtils is still called, even without a
- // PackageSetting to pass in.
- int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);
- int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);
-
- // REMOVE SharedUserSetting from method; update in a separate call
- pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
- originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
- destCodeFile, parsedPackage.getNativeLibraryRootDir(),
- AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
- AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
- parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
- true /*allowInstall*/, instantApp, virtualPreload,
- UserManagerService.getInstance(), usesSdkLibraries,
- parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries,
- parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
- newDomainSetId);
- } else {
- // make a deep copy to avoid modifying any existing system state.
- pkgSetting = new PackageSetting(pkgSetting);
- pkgSetting.setPkg(parsedPackage);
-
- // REMOVE SharedUserSetting from method; update in a separate call.
- //
- // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
- // secondaryCpuAbi are not known at this point so we always update them
- // to null here, only to reset them at a later point.
- Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
- destCodeFile, parsedPackage.getNativeLibraryDir(),
- AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
- AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
- PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
- PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
- UserManagerService.getInstance(),
- usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(),
- usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
- parsedPackage.getMimeGroups(), newDomainSetId);
- }
- if (createNewPackage && originalPkgSetting != null) {
- // This is the initial transition from the original package, so,
- // fix up the new package's name now. We must do this after looking
- // up the package under its new name, so getPackageLP takes care of
- // fiddling things correctly.
- parsedPackage.setPackageName(originalPkgSetting.getPackageName());
-
- // File a report about this.
- String msg = "New package " + pkgSetting.getRealName()
- + " renamed to replace old package " + pkgSetting.getPackageName();
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- }
-
- final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());
- // for existing packages, change the install state; but, only if it's explicitly specified
- if (!createNewPackage) {
- final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
- PackageManagerService.setInstantAppForUser(
- injector, pkgSetting, userId, instantApp, fullApp);
- }
- // TODO(patb): see if we can do away with disabled check here.
- if (disabledPkgSetting != null
- || (0 != (scanFlags & SCAN_NEW_INSTALL)
- && pkgSetting != null && pkgSetting.isSystem())) {
- pkgSetting.getPkgState().setUpdatedSystemApp(true);
- }
-
- parsedPackage.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,
- injector.getCompatibility()));
-
- if (parsedPackage.isSystem()) {
- configurePackageComponents(parsedPackage);
- }
-
- final String cpuAbiOverride = deriveAbiOverride(request.mCpuAbiOverride);
- final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
-
- final File appLib32InstallDir = PackageManagerService.getAppLib32InstallDir();
- if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
- if (needToDeriveAbi) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
- final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
- packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
- cpuAbiOverride, appLib32InstallDir);
- derivedAbi.first.applyTo(parsedPackage);
- derivedAbi.second.applyTo(parsedPackage);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
- // Some system apps still use directory structure for native libraries
- // in which case we might end up not detecting abi solely based on apk
- // structure. Try to detect abi based on directory structure.
-
- String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
- if (parsedPackage.isSystem() && !isUpdatedSystemApp
- && pkgRawPrimaryCpuAbi == null) {
- final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
- parsedPackage);
- abis.applyTo(parsedPackage);
- abis.applyTo(pkgSetting);
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
- isUpdatedSystemApp, appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
- }
- } else {
- // This is not a first boot or an upgrade, don't bother deriving the
- // ABI during the scan. Instead, trust the value that was stored in the
- // package setting.
- parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings)
- .setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
-
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
- isUpdatedSystemApp, appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
-
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG, "Using ABIS and native lib paths from settings : "
- + parsedPackage.getPackageName() + " "
- + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
- + ", "
- + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
- }
- }
- } else {
- if ((scanFlags & SCAN_MOVE) != 0) {
- // We haven't run dex-opt for this move (since we've moved the compiled output too)
- // but we already have this packages package info in the PackageSetting. We just
- // use that and derive the native library path based on the new code path.
- parsedPackage.setPrimaryCpuAbi(pkgSetting.getPrimaryCpuAbi())
- .setSecondaryCpuAbi(pkgSetting.getSecondaryCpuAbi());
- }
-
- // Set native library paths again. For moves, the path will be updated based on the
- // ABIs we've determined above. For non-moves, the path will be updated based on the
- // ABIs we determined during compilation, but the path will depend on the final
- // package path (after the rename away from the stage path).
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
- appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
- }
-
- // This is a special case for the "system" package, where the ABI is
- // dictated by the zygote configuration (and init.rc). We should keep track
- // of this ABI so that we can deal with "normal" applications that run under
- // the same UID correctly.
- if (isPlatformPackage) {
- parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit()
- ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);
- }
-
- // If there's a mismatch between the abi-override in the package setting
- // and the abiOverride specified for the install. Warn about this because we
- // would've already compiled the app without taking the package setting into
- // account.
- if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
- if (cpuAbiOverride == null) {
- Slog.w(TAG, "Ignoring persisted ABI override for package "
- + parsedPackage.getPackageName());
- }
- }
-
- pkgSetting.setPrimaryCpuAbi(AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage))
- .setSecondaryCpuAbi(AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage))
- .setCpuAbiOverride(cpuAbiOverride);
-
- if (DEBUG_ABI_SELECTION) {
- Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
- + " to root=" + parsedPackage.getNativeLibraryRootDir()
- + ", to dir=" + parsedPackage.getNativeLibraryDir()
- + ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());
- }
-
- // Push the derived path down into PackageSettings so we know what to
- // clean up at uninstall time.
- pkgSetting.setLegacyNativeLibraryPath(parsedPackage.getNativeLibraryRootDir());
-
- if (DEBUG_ABI_SELECTION) {
- Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
- + " primary=" + pkgSetting.getPrimaryCpuAbi()
- + " secondary=" + pkgSetting.getSecondaryCpuAbi()
- + " abiOverride=" + pkgSetting.getCpuAbiOverride());
- }
-
- if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.getSharedUser() != null) {
- // We don't do this here during boot because we can do it all
- // at once after scanning all existing packages.
- //
- // We also do this *before* we perform dexopt on this package, so that
- // we can avoid redundant dexopts, and also to make sure we've got the
- // code and package path correct.
- changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.getSharedUser(),
- parsedPackage, packageAbiHelper.getAdjustedAbiForSharedUser(
- pkgSetting.getSharedUser().packages, parsedPackage));
- }
-
- parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
- .contains(android.Manifest.permission.FACTORY_TEST));
-
- if (parsedPackage.isSystem()) {
- pkgSetting.setIsOrphaned(true);
- }
-
- // Take care of first install / last update times.
- final long scanFileTime = getLastModifiedTime(parsedPackage);
- if (currentTime != 0) {
- if (pkgSetting.getFirstInstallTime() == 0) {
- pkgSetting.setFirstInstallTime(currentTime)
- .setLastUpdateTime(currentTime);
- } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
- pkgSetting.setLastUpdateTime(currentTime);
- }
- } else if (pkgSetting.getFirstInstallTime() == 0) {
- // We need *something*. Take time time stamp of the file.
- pkgSetting.setFirstInstallTime(scanFileTime)
- .setLastUpdateTime(scanFileTime);
- } else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
- if (scanFileTime != pkgSetting.getLastModifiedTime()) {
- // A package on the system image has changed; consider this
- // to be an update.
- pkgSetting.setLastUpdateTime(scanFileTime);
- }
- }
- pkgSetting.setLastModifiedTime(scanFileTime);
- // TODO(b/135203078): Remove, move to constructor
- pkgSetting.setPkg(parsedPackage)
- .setFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting))
- .setPrivateFlags(
- PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting));
- if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) {
- pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode());
- }
- // Update volume if needed
- final String volumeUuid = parsedPackage.getVolumeUuid();
- if (!Objects.equals(volumeUuid, pkgSetting.getVolumeUuid())) {
- Slog.i(PackageManagerService.TAG,
- "Update" + (pkgSetting.isSystem() ? " system" : "")
- + " package " + parsedPackage.getPackageName()
- + " volume from " + pkgSetting.getVolumeUuid()
- + " to " + volumeUuid);
- pkgSetting.setVolumeUuid(volumeUuid);
- }
-
- SharedLibraryInfo sdkLibraryInfo = null;
- if (!TextUtils.isEmpty(parsedPackage.getSdkLibName())) {
- sdkLibraryInfo = AndroidPackageUtils.createSharedLibraryForSdk(parsedPackage);
- }
- SharedLibraryInfo staticSharedLibraryInfo = null;
- if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
- staticSharedLibraryInfo =
- AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
- }
- List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
- if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
- dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
- for (String name : parsedPackage.getLibraryNames()) {
- dynamicSharedLibraryInfos.add(
- AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));
- }
- }
-
- return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
- !createNewPackage /* existingSettingCopied */,
- previousAppId, sdkLibraryInfo, staticSharedLibraryInfo,
- dynamicSharedLibraryInfos);
- }
-
- /**
- * Returns the actual scan flags depending upon the state of the other settings.
- * <p>Updated system applications will not have the following flags set
- * by default and need to be adjusted after the fact:
- * <ul>
- * <li>{@link PackageManagerService.SCAN_AS_SYSTEM}</li>
- * <li>{@link PackageManagerService.SCAN_AS_PRIVILEGED}</li>
- * <li>{@link PackageManagerService.SCAN_AS_OEM}</li>
- * <li>{@link PackageManagerService.SCAN_AS_VENDOR}</li>
- * <li>{@link PackageManagerService.SCAN_AS_PRODUCT}</li>
- * <li>{@link PackageManagerService.SCAN_AS_SYSTEM_EXT}</li>
- * <li>{@link PackageManagerService.SCAN_AS_INSTANT_APP}</li>
- * <li>{@link PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD}</li>
- * <li>{@link PackageManagerService.SCAN_AS_ODM}</li>
- * </ul>
- */
- private @PackageManagerService.ScanFlags int adjustScanFlags(
- @PackageManagerService.ScanFlags int scanFlags,
- PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
- AndroidPackage pkg) {
-
- // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
- // the correct isSystem value now that we don't disable system packages before scan.
- final PackageSetting systemPkgSetting =
- (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
- && pkgSetting != null && pkgSetting.isSystem()
- ? pkgSetting
- : disabledPkgSetting;
- if (systemPkgSetting != null) {
- // updated system application, must at least have SCAN_AS_SYSTEM
- scanFlags |= SCAN_AS_SYSTEM;
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
- scanFlags |= SCAN_AS_OEM;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
- scanFlags |= SCAN_AS_VENDOR;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
- scanFlags |= SCAN_AS_PRODUCT;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
- scanFlags |= SCAN_AS_SYSTEM_EXT;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
- scanFlags |= SCAN_AS_ODM;
- }
- }
- if (pkgSetting != null) {
- final int userId = ((user == null) ? 0 : user.getIdentifier());
- if (pkgSetting.getInstantApp(userId)) {
- scanFlags |= SCAN_AS_INSTANT_APP;
- }
- if (pkgSetting.getVirtualPreload(userId)) {
- scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
- }
- }
-
- // Scan as privileged apps that share a user with a priv-app.
- final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
- && getVendorPartitionVersion() < 28;
- if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
- && !pkg.isPrivileged()
- && (pkg.getSharedUserId() != null)
- && !skipVendorPrivilegeScan) {
- SharedUserSetting sharedUserSetting = null;
- try {
- sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0,
- 0, false);
- } catch (PackageManagerException ignore) {
- }
- if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
- // Exempt SharedUsers signed with the platform key.
- // TODO(b/72378145) Fix this exemption. Force signature apps
- // to allowlist their privileged permissions just like other
- // priv-apps.
- synchronized (mPm.mLock) {
- PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
- if ((compareSignatures(
- platformPkgSetting.getSigningDetails().getSignatures(),
- pkg.getSigningDetails().getSignatures())
- != PackageManager.SIGNATURE_MATCH)) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- }
- }
- }
-
- return scanFlags;
- }
-
- public Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
- @ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user) throws PackageManagerException {
- final boolean scanSystemPartition =
- (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
- final String renamedPkgName;
- final PackageSetting disabledPkgSetting;
- final boolean isSystemPkgUpdated;
- final boolean pkgAlreadyExists;
- PackageSetting pkgSetting;
- AndroidPackage platformPackage;
- final boolean isUpgrade = mPm.isDeviceUpgrading();
-
- synchronized (mPm.mLock) {
- platformPackage = mPm.getPlatformPackage();
- renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
- AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
- final String realPkgName = getRealPackageName(parsedPackage,
- renamedPkgName);
- if (realPkgName != null) {
- ensurePackageRenamed(parsedPackage, renamedPkgName);
- }
- final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
- renamedPkgName);
- final PackageSetting installedPkgSetting = mPm.mSettings.getPackageLPr(
- parsedPackage.getPackageName());
- pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
- pkgAlreadyExists = pkgSetting != null;
- final String disabledPkgName = pkgAlreadyExists
- ? pkgSetting.getPackageName() : parsedPackage.getPackageName();
- if (scanSystemPartition && !pkgAlreadyExists
- && mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
- // The updated-package data for /system apk remains inconsistently
- // after the package data for /data apk is lost accidentally.
- // To recover it, enable /system apk and install it as non-updated system app.
- Slog.w(TAG, "Inconsistent package setting of updated system app for "
- + disabledPkgName + ". To recover it, enable the system app"
- + "and install it as non-updated system app.");
- mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
- }
- disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
- isSystemPkgUpdated = disabledPkgSetting != null;
-
- if (DEBUG_INSTALL && isSystemPkgUpdated) {
- Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
- }
-
- final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
- ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
- 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
- : null;
- if (DEBUG_PACKAGE_SCANNING
- && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
- && sharedUserSetting != null) {
- Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
- + " (uid=" + sharedUserSetting.userId + "):"
- + " packages=" + sharedUserSetting.packages);
- }
-
- if (scanSystemPartition) {
- if (isSystemPkgUpdated) {
- // we're updating the disabled package, so, scan it as the package setting
- boolean isPlatformPackage = platformPackage != null
- && platformPackage.getPackageName().equals(
- parsedPackage.getPackageName());
- final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
- null, disabledPkgSetting /* pkgSetting */,
- null /* disabledPkgSetting */, null /* originalPkgSetting */,
- null, parseFlags, scanFlags, isPlatformPackage, user, null);
- applyPolicy(parsedPackage, scanFlags,
- platformPackage, true);
- final ScanResult scanResult =
- scanPackageOnlyLI(request, mInjector,
- mPm.mFactoryTest, -1L);
- if (scanResult.mExistingSettingCopied
- && scanResult.mRequest.mPkgSetting != null) {
- scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
- }
- }
- }
- }
-
- final boolean newPkgChangedPaths = pkgAlreadyExists
- && !pkgSetting.getPathString().equals(parsedPackage.getPath());
- final boolean newPkgVersionGreater = pkgAlreadyExists
- && parsedPackage.getLongVersionCode() > pkgSetting.getVersionCode();
- final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
- && newPkgChangedPaths && newPkgVersionGreater;
- if (isSystemPkgBetter) {
- // The version of the application on /system is greater than the version on
- // /data. Switch back to the application on /system.
- // It's safe to assume the application on /system will correctly scan. If not,
- // there won't be a working copy of the application.
- synchronized (mPm.mLock) {
- // just remove the loaded entries from package lists
- mPm.mPackages.remove(pkgSetting.getPackageName());
- }
-
- logCriticalInfo(Log.WARN,
- "System package updated;"
- + " name: " + pkgSetting.getPackageName()
- + "; " + pkgSetting.getVersionCode() + " --> "
- + parsedPackage.getLongVersionCode()
- + "; " + pkgSetting.getPathString()
- + " --> " + parsedPackage.getPath());
-
- final InstallArgs args = new FileInstallArgs(
- pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()), mPm);
- args.cleanUpResourcesLI();
- synchronized (mPm.mLock) {
- mPm.mSettings.enableSystemPackageLPw(pkgSetting.getPackageName());
- }
- }
-
- // The version of the application on the /system partition is less than or
- // equal to the version on the /data partition. Throw an exception and use
- // the application already installed on the /data partition.
- if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
- // In the case of a skipped package, commitReconciledScanResultLocked is not called to
- // add the object to the "live" data structures, so this is the final mutation step
- // for the package. Which means it needs to be finalized here to cache derived fields.
- // This is relevant for cases where the disabled system package is used for flags or
- // other metadata.
- parsedPackage.hideAsFinal();
- throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
- + " at " + parsedPackage.getPath() + " ignored: updated version "
- + (pkgAlreadyExists ? String.valueOf(pkgSetting.getVersionCode()) : "unknown")
- + " better than this " + parsedPackage.getLongVersionCode());
- }
-
- // Verify certificates against what was last scanned. Force re-collecting certificate in two
- // special cases:
- // 1) when scanning system, force re-collect only if system is upgrading.
- // 2) when scannning /data, force re-collect only if the app is privileged (updated from
- // preinstall, or treated as privileged, e.g. due to shared user ID).
- final boolean forceCollect = scanSystemPartition ? isUpgrade
- : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
- if (DEBUG_VERIFY && forceCollect) {
- Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
- }
-
- // Full APK verification can be skipped during certificate collection, only if the file is
- // in verified partition, or can be verified on access (when apk verity is enabled). In both
- // cases, only data in Signing Block is verified instead of the whole file.
- // TODO(b/136132412): skip for Incremental installation
- final boolean skipVerify = scanSystemPartition
- || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
- collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify);
-
- // Reset profile if the application version is changed
- maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
-
- /*
- * A new system app appeared, but we already had a non-system one of the
- * same name installed earlier.
- */
- boolean shouldHideSystemApp = false;
- // A new application appeared on /system, but, we already have a copy of
- // the application installed on /data.
- if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
- && !pkgSetting.isSystem()) {
-
- if (!parsedPackage.getSigningDetails()
- .checkCapability(pkgSetting.getSigningDetails(),
- SigningDetails.CertCapabilities.INSTALLED_DATA)
- && !pkgSetting.getSigningDetails().checkCapability(
- parsedPackage.getSigningDetails(),
- SigningDetails.CertCapabilities.ROLLBACK)) {
- logCriticalInfo(Log.WARN,
- "System package signature mismatch;"
- + " name: " + pkgSetting.getPackageName());
- try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
- parsedPackage.getPackageName(),
- "scanPackageInternalLI")) {
- DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
- deletePackageHelper.deletePackageLIF(parsedPackage.getPackageName(), null, true,
- mPm.mUserManager.getUserIds(), 0, null, false);
- }
- pkgSetting = null;
- } else if (newPkgVersionGreater) {
- // The application on /system is newer than the application on /data.
- // Simply remove the application on /data [keeping application data]
- // and replace it with the version on /system.
- logCriticalInfo(Log.WARN,
- "System package enabled;"
- + " name: " + pkgSetting.getPackageName()
- + "; " + pkgSetting.getVersionCode() + " --> "
- + parsedPackage.getLongVersionCode()
- + "; " + pkgSetting.getPathString() + " --> "
- + parsedPackage.getPath());
- InstallArgs args = new FileInstallArgs(
- pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()),
- mPm);
- synchronized (mPm.mInstallLock) {
- args.cleanUpResourcesLI();
- }
- } else {
- // The application on /system is older than the application on /data. Hide
- // the application on /system and the version on /data will be scanned later
- // and re-added like an update.
- shouldHideSystemApp = true;
- logCriticalInfo(Log.INFO,
- "System package disabled;"
- + " name: " + pkgSetting.getPackageName()
- + "; old: " + pkgSetting.getPathString() + " @ "
- + pkgSetting.getVersionCode()
- + "; new: " + parsedPackage.getPath() + " @ "
- + parsedPackage.getPath());
- }
- }
-
- final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags,
- scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
- return new Pair<>(scanResult, shouldHideSystemApp);
- }
-
- /**
- * Returns if forced apk verification can be skipped for the whole package, including splits.
- */
- private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
- final String packageName = pkg.getPackageName();
- if (!canSkipForcedApkVerification(packageName, pkg.getBaseApkPath())) {
- return false;
- }
- // TODO: Allow base and splits to be verified individually.
- String[] splitCodePaths = pkg.getSplitCodePaths();
- if (!ArrayUtils.isEmpty(splitCodePaths)) {
- for (int i = 0; i < splitCodePaths.length; i++) {
- if (!canSkipForcedApkVerification(packageName, splitCodePaths[i])) {
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
- * whether the apk contains signed root hash. Note that the signer's certificate still needs to
- * match one in a trusted source, and should be done separately.
- */
- private boolean canSkipForcedApkVerification(String packageName, String apkPath) {
- if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
- return VerityUtils.hasFsverity(apkPath);
- }
-
- try {
- final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
- if (rootHashObserved == null) {
- return false; // APK does not contain Merkle tree root hash.
- }
- synchronized (mPm.mInstallLock) {
- // Returns whether the observed root hash matches what kernel has.
- mPm.mInstaller.assertFsverityRootHashMatches(packageName, apkPath,
- rootHashObserved);
- return true;
- }
- } catch (Installer.InstallerException | IOException | DigestException
- | NoSuchAlgorithmException e) {
- Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
- }
- return false;
- }
-
- private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
- boolean forceCollect, boolean skipVerify) throws PackageManagerException {
- // When upgrading from pre-N MR1, verify the package time stamp using the package
- // directory and not the APK file.
- final long lastModifiedTime = mPm.isPreNMR1Upgrade()
- ? new File(parsedPackage.getPath()).lastModified()
- : getLastModifiedTime(parsedPackage);
- final Settings.VersionInfo settingsVersionForPackage =
- mPm.getSettingsVersionForPackage(parsedPackage);
- if (ps != null && !forceCollect
- && ps.getPathString().equals(parsedPackage.getPath())
- && ps.getLastModifiedTime() == lastModifiedTime
- && !InstallPackageHelper.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
- && !InstallPackageHelper.isRecoverSignatureUpdateNeeded(
- settingsVersionForPackage)) {
- if (ps.getSigningDetails().getSignatures() != null
- && ps.getSigningDetails().getSignatures().length != 0
- && ps.getSigningDetails().getSignatureSchemeVersion()
- != SigningDetails.SignatureSchemeVersion.UNKNOWN) {
- // Optimization: reuse the existing cached signing data
- // if the package appears to be unchanged.
- parsedPackage.setSigningDetails(
- new SigningDetails(ps.getSigningDetails()));
- return;
- }
-
- Slog.w(TAG, "PackageSetting for " + ps.getPackageName()
- + " is missing signatures. Collecting certs again to recover them.");
- } else {
- Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
- + (forceCollect ? " (forced)" : ""));
- }
-
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
- final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
- input, parsedPackage, skipVerify);
- if (result.isError()) {
- throw new PackageManagerException(
- result.getErrorCode(), result.getErrorMessage(), result.getException());
- }
- parsedPackage.setSigningDetails(result.getResult());
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- /**
- * Clear the package profile if this was an upgrade and the package
- * version was updated.
- */
- private void maybeClearProfilesForUpgradesLI(
- @Nullable PackageSetting originalPkgSetting,
- @NonNull AndroidPackage pkg) {
- if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) {
- return;
- }
- if (originalPkgSetting.getVersionCode() == pkg.getLongVersionCode()) {
- return;
- }
-
- final AppDataHelper appDataHelper = new AppDataHelper(mPm);
- appDataHelper.clearAppProfilesLIF(pkg);
- if (DEBUG_INSTALL) {
- Slog.d(TAG, originalPkgSetting.getPackageName()
- + " clear profile due to version change "
- + originalPkgSetting.getVersionCode() + " != "
- + pkg.getLongVersionCode());
- }
- }
-
- /**
- * Returns the original package setting.
- * <p>A package can migrate its name during an update. In this scenario, a package
- * designates a set of names that it considers as one of its original names.
- * <p>An original package must be signed identically and it must have the same
- * shared user [if any].
- */
- @GuardedBy("mPm.mLock")
- @Nullable
- private PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- if (isPackageRenamed(pkg, renamedPkgName)) {
- return null;
- }
- for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
- final PackageSetting originalPs =
- mPm.mSettings.getPackageLPr(pkg.getOriginalPackages().get(i));
- if (originalPs != null) {
- // the package is already installed under its original name...
- // but, should we use it?
- if (!verifyPackageUpdateLPr(originalPs, pkg)) {
- // the new package is incompatible with the original
- continue;
- } else if (originalPs.getSharedUser() != null) {
- if (!originalPs.getSharedUser().name.equals(pkg.getSharedUserId())) {
- // the shared user id is incompatible with the original
- Slog.w(TAG, "Unable to migrate data from " + originalPs.getPackageName()
- + " to " + pkg.getPackageName() + ": old uid "
- + originalPs.getSharedUser().name
- + " differs from " + pkg.getSharedUserId());
- continue;
- }
- // TODO: Add case when shared user id is added [b/28144775]
- } else {
- if (DEBUG_UPGRADE) {
- Log.v(TAG, "Renaming new package "
- + pkg.getPackageName() + " to old name "
- + originalPs.getPackageName());
- }
- }
- return originalPs;
- }
- }
- return null;
- }
-
- @GuardedBy("mPm.mLock")
- private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
- if ((oldPkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) == 0) {
- Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
- + " to " + newPkg.getPackageName()
- + ": old package not in system partition");
- return false;
- } else if (mPm.mPackages.get(oldPkg.getPackageName()) != null) {
- Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
- + " to " + newPkg.getPackageName()
- + ": old package still exists");
- return false;
- }
- return true;
- }
-
- /**
- * Asserts the parsed package is valid according to the given policy. If the
- * package is invalid, for whatever reason, throws {@link PackageManagerException}.
- * <p>
- * Implementation detail: This method must NOT have any side effects. It would
- * ideally be static, but, it requires locks to read system state.
- *
- * @throws PackageManagerException If the package fails any of the validation checks
- */
- private void assertPackageIsValid(AndroidPackage pkg,
- final @ParsingPackageUtils.ParseFlags int parseFlags,
- final @PackageManagerService.ScanFlags int scanFlags)
- throws PackageManagerException {
- if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
- assertCodePolicy(pkg);
- }
-
- if (pkg.getPath() == null) {
- // Bail out. The resource and code paths haven't been set.
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Code and resource paths haven't been set correctly");
- }
-
- // Check that there is an APEX package with the same name only during install/first boot
- // after OTA.
- final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
- final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
- if ((isUserInstall || isFirstBootOrUpgrade)
- && mPm.mApexManager.isApexPackage(pkg.getPackageName())) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- pkg.getPackageName()
- + " is an APEX package and can't be installed as an APK.");
- }
-
- // Make sure we're not adding any bogus keyset info
- final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
- ksms.assertScannedPackageValid(pkg);
-
- synchronized (mPm.mLock) {
- // The special "android" package can only be defined once
- if (pkg.getPackageName().equals("android")) {
- if (mPm.getCoreAndroidApplication() != null) {
- Slog.w(TAG, "*************************************************");
- Slog.w(TAG, "Core android package being redefined. Skipping.");
- Slog.w(TAG, " codePath=" + pkg.getPath());
- Slog.w(TAG, "*************************************************");
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Core android package being redefined. Skipping.");
- }
- }
-
- // A package name must be unique; don't allow duplicates
- if ((scanFlags & SCAN_NEW_INSTALL) == 0
- && mPm.mPackages.containsKey(pkg.getPackageName())) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Application package " + pkg.getPackageName()
- + " already installed. Skipping duplicate.");
- }
-
- if (pkg.isStaticSharedLibrary()) {
- // Static libs have a synthetic package name containing the version
- // but we still want the base name to be unique.
- if ((scanFlags & SCAN_NEW_INSTALL) == 0
- && mPm.mPackages.containsKey(pkg.getManifestPackageName())) {
- throw new PackageManagerException(
- "Duplicate static shared lib provider package");
- }
-
- // Static shared libraries should have at least O target SDK
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs must target O SDK or higher");
- }
-
- // Package declaring static a shared lib cannot be instant apps
- if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot be instant apps");
- }
-
- // Package declaring static a shared lib cannot be renamed since the package
- // name is synthetic and apps can't code around package manager internals.
- if (!ArrayUtils.isEmpty(pkg.getOriginalPackages())) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot be renamed");
- }
-
- // Package declaring static a shared lib cannot declare dynamic libs
- if (!ArrayUtils.isEmpty(pkg.getLibraryNames())) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot declare dynamic libs");
- }
-
- // Package declaring static a shared lib cannot declare shared users
- if (pkg.getSharedUserId() != null) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot declare shared users");
- }
-
- // Static shared libs cannot declare activities
- if (!pkg.getActivities().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare activities");
- }
-
- // Static shared libs cannot declare services
- if (!pkg.getServices().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare services");
- }
-
- // Static shared libs cannot declare providers
- if (!pkg.getProviders().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare content providers");
- }
-
- // Static shared libs cannot declare receivers
- if (!pkg.getReceivers().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare broadcast receivers");
- }
-
- // Static shared libs cannot declare permission groups
- if (!pkg.getPermissionGroups().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare permission groups");
- }
-
- // Static shared libs cannot declare attributions
- if (!pkg.getAttributions().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare features");
- }
-
- // Static shared libs cannot declare permissions
- if (!pkg.getPermissions().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare permissions");
- }
-
- // Static shared libs cannot declare protected broadcasts
- if (!pkg.getProtectedBroadcasts().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare protected broadcasts");
- }
-
- // Static shared libs cannot be overlay targets
- if (pkg.getOverlayTarget() != null) {
- throw new PackageManagerException(
- "Static shared libs cannot be overlay targets");
- }
-
- // The version codes must be ordered as lib versions
- long minVersionCode = Long.MIN_VALUE;
- long maxVersionCode = Long.MAX_VALUE;
-
- WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mPm.mSharedLibraries.get(
- pkg.getStaticSharedLibName());
- if (versionedLib != null) {
- final int versionCount = versionedLib.size();
- for (int i = 0; i < versionCount; i++) {
- SharedLibraryInfo libInfo = versionedLib.valueAt(i);
- final long libVersionCode = libInfo.getDeclaringPackage()
- .getLongVersionCode();
- if (libInfo.getLongVersion() < pkg.getStaticSharedLibVersion()) {
- minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
- } else if (libInfo.getLongVersion()
- > pkg.getStaticSharedLibVersion()) {
- maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
- } else {
- minVersionCode = maxVersionCode = libVersionCode;
- break;
- }
- }
- }
- if (pkg.getLongVersionCode() < minVersionCode
- || pkg.getLongVersionCode() > maxVersionCode) {
- throw new PackageManagerException("Static shared"
- + " lib version codes must be ordered as lib versions");
- }
- }
-
- // If we're only installing presumed-existing packages, require that the
- // scanned APK is both already known and at the path previously established
- // for it. Previously unknown packages we pick up normally, but if we have an
- // a priori expectation about this package's install presence, enforce it.
- // With a singular exception for new system packages. When an OTA contains
- // a new system package, we allow the codepath to change from a system location
- // to the user-installed location. If we don't allow this change, any newer,
- // user-installed version of the application will be ignored.
- if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
- if (mPm.isExpectingBetter(pkg.getPackageName())) {
- Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
- + pkg.getPackageName());
- } else {
- PackageSetting known = mPm.mSettings.getPackageLPr(pkg.getPackageName());
- if (known != null) {
- if (DEBUG_PACKAGE_SCANNING) {
- Log.d(TAG, "Examining " + pkg.getPath()
- + " and requiring known path " + known.getPathString());
- }
- if (!pkg.getPath().equals(known.getPathString())) {
- throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
- "Application package " + pkg.getPackageName()
- + " found at " + pkg.getPath()
- + " but expected at " + known.getPathString()
- + "; ignoring.");
- }
- } else {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- "Application package " + pkg.getPackageName()
- + " not found; ignoring.");
- }
- }
- }
-
- // Verify that this new package doesn't have any content providers
- // that conflict with existing packages. Only do this if the
- // package isn't already installed, since we don't want to break
- // things that are installed.
- if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
- mPm.mComponentResolver.assertProvidersNotDefined(pkg);
- }
-
- // If this package has defined explicit processes, then ensure that these are
- // the only processes used by its components.
- final Map<String, ParsedProcess> procs = pkg.getProcesses();
- if (!procs.isEmpty()) {
- if (!procs.containsKey(pkg.getProcessName())) {
- throw new PackageManagerException(
- INSTALL_FAILED_PROCESS_NOT_DEFINED,
- "Can't install because application tag's process attribute "
- + pkg.getProcessName()
- + " (in package " + pkg.getPackageName()
- + ") is not included in the <processes> list");
- }
- assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
- assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
- assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
- assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
- }
-
- // Verify that packages sharing a user with a privileged app are marked as privileged.
- if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
- SharedUserSetting sharedUserSetting = null;
- try {
- sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(),
- 0, 0, false);
- } catch (PackageManagerException ignore) {
- }
- if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
- // Exempt SharedUsers signed with the platform key.
- PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
- if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Apps that share a user with a "
- + "privileged app must themselves be marked as privileged. "
- + pkg.getPackageName() + " shares privileged user "
- + pkg.getSharedUserId() + ".");
- }
- }
- }
-
- // Apply policies specific for runtime resource overlays (RROs).
- if (pkg.getOverlayTarget() != null) {
- // System overlays have some restrictions on their use of the 'static' state.
- if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
- // We are scanning a system overlay. This can be the first scan of the
- // system/vendor/oem partition, or an update to the system overlay.
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- // This must be an update to a system overlay. Immutable overlays cannot be
- // upgraded.
- if (!mPm.isOverlayMutable(pkg.getPackageName())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
- + " is static and cannot be upgraded.");
- }
- } else {
- if ((scanFlags & SCAN_AS_VENDOR) != 0) {
- if (pkg.getTargetSdkVersion() < getVendorPartitionVersion()) {
- Slog.w(TAG, "System overlay " + pkg.getPackageName()
- + " targets an SDK below the required SDK level of vendor"
- + " overlays (" + getVendorPartitionVersion() + ")."
- + " This will become an install error in a future release");
- }
- } else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
- Slog.w(TAG, "System overlay " + pkg.getPackageName()
- + " targets an SDK below the required SDK level of system"
- + " overlays (" + Build.VERSION.SDK_INT + ")."
- + " This will become an install error in a future release");
- }
- }
- } else {
- // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
- // signed with the platform certificate. Check this in increasing order of
- // computational cost.
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) {
- final PackageSetting platformPkgSetting =
- mPm.mSettings.getPackageLPr("android");
- if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
- + " must target Q or later, "
- + "or be signed with the platform certificate");
- }
- }
-
- // A non-preloaded overlay package, without <overlay android:targetName>, will
- // only be used if it is signed with the same certificate as its target OR if
- // it is signed with the same certificate as a reference package declared
- // in 'overlay-config-signature' tag of SystemConfig.
- // If the target is already installed or 'overlay-config-signature' tag in
- // SystemConfig is set, check this here to augment the last line of defense
- // which is OMS.
- if (pkg.getOverlayTargetOverlayableName() == null) {
- final PackageSetting targetPkgSetting =
- mPm.mSettings.getPackageLPr(pkg.getOverlayTarget());
- if (targetPkgSetting != null) {
- if (!comparePackageSignatures(targetPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- // check reference signature
- if (mPm.mOverlayConfigSignaturePackage == null) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName() + " and target "
- + pkg.getOverlayTarget() + " signed with"
- + " different certificates, and the overlay lacks"
- + " <overlay android:targetName>");
- }
- final PackageSetting refPkgSetting =
- mPm.mSettings.getPackageLPr(
- mPm.mOverlayConfigSignaturePackage);
- if (!comparePackageSignatures(refPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName() + " signed with a different "
- + "certificate than both the reference package and "
- + "target " + pkg.getOverlayTarget() + ", and the "
- + "overlay lacks <overlay android:targetName>");
- }
- }
- }
- }
- }
- }
-
- // If the package is not on a system partition ensure it is signed with at least the
- // minimum signature scheme version required for its target SDK.
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- int minSignatureSchemeVersion =
- ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
- pkg.getTargetSdkVersion());
- if (pkg.getSigningDetails().getSignatureSchemeVersion()
- < minSignatureSchemeVersion) {
- throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No signature found in package of version " + minSignatureSchemeVersion
- + " or newer for package " + pkg.getPackageName());
- }
- }
- }
- }
-
- private static <T extends ParsedMainComponent> void assertPackageProcesses(AndroidPackage pkg,
- List<T> components, Map<String, ParsedProcess> procs, String compName)
- throws PackageManagerException {
- if (components == null) {
- return;
- }
- for (int i = components.size() - 1; i >= 0; i--) {
- final ParsedMainComponent component = components.get(i);
- if (!procs.containsKey(component.getProcessName())) {
- throw new PackageManagerException(
- INSTALL_FAILED_PROCESS_NOT_DEFINED,
- "Can't install because " + compName + " " + component.getClassName()
- + "'s process attribute " + component.getProcessName()
- + " (in package " + pkg.getPackageName()
- + ") is not included in the <processes> list");
- }
- }
- }
-
- /**
- * Applies the adjusted ABI calculated by
- * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all
- * relevant packages and settings.
- * @param sharedUserSetting The {@code SharedUserSetting} to adjust
- * @param scannedPackage the package being scanned or null
- * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}
- * @return the list of code paths that belong to packages that had their ABIs adjusted.
- */
- public static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
- ParsedPackage scannedPackage, String adjustedAbi) {
- if (scannedPackage != null) {
- scannedPackage.setPrimaryCpuAbi(adjustedAbi);
- }
- List<String> changedAbiCodePath = null;
- for (PackageSetting ps : sharedUserSetting.packages) {
- if (scannedPackage == null
- || !scannedPackage.getPackageName().equals(ps.getPackageName())) {
- if (ps.getPrimaryCpuAbi() != null) {
- continue;
- }
-
- ps.setPrimaryCpuAbi(adjustedAbi);
- if (ps.getPkg() != null) {
- if (!TextUtils.equals(adjustedAbi,
- AndroidPackageUtils.getRawPrimaryCpuAbi(ps.getPkg()))) {
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG,
- "Adjusting ABI for " + ps.getPackageName() + " to "
- + adjustedAbi + " (scannedPackage="
- + (scannedPackage != null ? scannedPackage : "null")
- + ")");
- }
- if (changedAbiCodePath == null) {
- changedAbiCodePath = new ArrayList<>();
- }
- changedAbiCodePath.add(ps.getPathString());
- }
- }
- }
- }
- return changedAbiCodePath;
- }
-
- /**
- * Applies policy to the parsed package based upon the given policy flags.
- * Ensures the package is in a good state.
- * <p>
- * Implementation detail: This method must NOT have any side effect. It would
- * ideally be static, but, it requires locks to read system state.
- */
- private static void applyPolicy(ParsedPackage parsedPackage,
- final @PackageManagerService.ScanFlags int scanFlags, AndroidPackage platformPkg,
- boolean isUpdatedSystemApp) {
- if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
- parsedPackage.setSystem(true);
- // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
- // is set during parse.
- if (parsedPackage.isDirectBootAware()) {
- parsedPackage.setAllComponentsDirectBootAware(true);
- }
- if (compressedFileExists(parsedPackage.getPath())) {
- parsedPackage.setStub(true);
- }
- } else {
- parsedPackage
- // Non system apps cannot mark any broadcast as protected
- .clearProtectedBroadcasts()
- // non system apps can't be flagged as core
- .setCoreApp(false)
- // clear flags not applicable to regular apps
- .setPersistent(false)
- .setDefaultToDeviceProtectedStorage(false)
- .setDirectBootAware(false)
- // non system apps can't have permission priority
- .capPermissionPriorities();
- }
- if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
- parsedPackage
- .markNotActivitiesAsNotExportedIfSingleUser();
- }
-
- parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
- .setOem((scanFlags & SCAN_AS_OEM) != 0)
- .setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
- .setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
- .setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
- .setOdm((scanFlags & SCAN_AS_ODM) != 0);
-
- // Check if the package is signed with the same key as the platform package.
- parsedPackage.setSignedWithPlatformKey(
- (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
- || (platformPkg != null && compareSignatures(
- platformPkg.getSigningDetails().getSignatures(),
- parsedPackage.getSigningDetails().getSignatures()
- ) == PackageManager.SIGNATURE_MATCH))
- );
-
- if (!parsedPackage.isSystem()) {
- // Only system apps can use these features.
- parsedPackage.clearOriginalPackages()
- .clearAdoptPermissions();
- }
-
- PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
- }
-
- /**
- * Enforces code policy for the package. This ensures that if an APK has
- * declared hasCode="true" in its manifest that the APK actually contains
- * code.
- *
- * @throws PackageManagerException If bytecode could not be found when it should exist
- */
- private static void assertCodePolicy(AndroidPackage pkg)
- throws PackageManagerException {
- final boolean shouldHaveCode = pkg.isHasCode();
- if (shouldHaveCode && !apkHasCode(pkg.getBaseApkPath())) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Package " + pkg.getBaseApkPath() + " code is missing");
- }
-
- if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
- for (int i = 0; i < pkg.getSplitCodePaths().length; i++) {
- final boolean splitShouldHaveCode =
- (pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
- if (splitShouldHaveCode && !apkHasCode(pkg.getSplitCodePaths()[i])) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Package " + pkg.getSplitCodePaths()[i] + " code is missing");
- }
- }
- }
- }
-
- /**
- * Returns the "real" name of the package.
- * <p>This may differ from the package's actual name if the application has already
- * been installed under one of this package's original names.
- */
- private static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- if (isPackageRenamed(pkg, renamedPkgName)) {
- return AndroidPackageUtils.getRealPackageOrNull(pkg);
- }
- return null;
- }
-
- /** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
- private static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- return pkg.getOriginalPackages().contains(renamedPkgName);
- }
-
- /**
- * Renames the package if it was installed under a different name.
- * <p>When we've already installed the package under an original name, update
- * the new package so we can continue to have the old name.
- */
- private static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
- @NonNull String renamedPackageName) {
- if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
- || parsedPackage.getPackageName().equals(renamedPackageName)) {
- return;
- }
- parsedPackage.setPackageName(renamedPackageName);
- }
-
- /**
- * Returns {@code true} if the given file contains code. Otherwise {@code false}.
- */
- private static boolean apkHasCode(String fileName) {
- StrictJarFile jarFile = null;
- try {
- jarFile = new StrictJarFile(fileName,
- false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
- return jarFile.findEntry("classes.dex") != null;
- } catch (IOException ignore) {
- } finally {
- try {
- if (jarFile != null) {
- jarFile.close();
- }
- } catch (IOException ignore) {
- }
- }
- return false;
- }
-
- /**
- * Sets the enabled state of components configured through {@link SystemConfig}.
- * This modifies the {@link PackageSetting} object.
- *
- * TODO(b/135203078): Move this to package parsing
- **/
- private static void configurePackageComponents(AndroidPackage pkg) {
- final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
- .getComponentsEnabledStates(pkg.getPackageName());
- if (componentsEnabledStates == null) {
- return;
- }
-
- for (int i = ArrayUtils.size(pkg.getActivities()) - 1; i >= 0; i--) {
- final ParsedActivity component = pkg.getActivities().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- ComponentMutateUtils.setEnabled(component, enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getReceivers()) - 1; i >= 0; i--) {
- final ParsedActivity component = pkg.getReceivers().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- ComponentMutateUtils.setEnabled(component, enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getProviders()) - 1; i >= 0; i--) {
- final ParsedProvider component = pkg.getProviders().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- ComponentMutateUtils.setEnabled(component, enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getServices()) - 1; i >= 0; i--) {
- final ParsedService component = pkg.getServices().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- ComponentMutateUtils.setEnabled(component, enabled);
- }
- }
- }
-
- private static int getVendorPartitionVersion() {
- final String version = SystemProperties.get("ro.vndk.version");
- if (!version.isEmpty()) {
- try {
- return Integer.parseInt(version);
- } catch (NumberFormatException ignore) {
- if (ArrayUtils.contains(Build.VERSION.ACTIVE_CODENAMES, version)) {
- return Build.VERSION_CODES.CUR_DEVELOPMENT;
- }
- }
- }
- return Build.VERSION_CODES.P;
- }
-}
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
new file mode 100644
index 000000000000..378c9e07566e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -0,0 +1,1006 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_ABI_SELECTION;
+import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
+import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
+import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
+import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
+import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
+import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
+import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_TIME;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
+import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
+import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.component.ComponentMutateUtils;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Process;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.apk.ApkSignatureVerifier;
+import android.util.jar.StrictJarFile;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+import dalvik.system.VMRuntime;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Helper class that handles package scanning logic
+ */
+final class ScanPackageUtils {
+ /**
+ * Just scans the package without any side effects.
+ *
+ * @param injector injector for acquiring dependencies
+ * @param request Information about the package to be scanned
+ * @param isUnderFactoryTest Whether or not the device is under factory test
+ * @param currentTime The current time, in millis
+ * @return The results of the scan
+ */
+ @GuardedBy("mPm.mInstallLock")
+ @VisibleForTesting
+ @NonNull
+ public static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+ PackageManagerServiceInjector injector,
+ boolean isUnderFactoryTest, long currentTime)
+ throws PackageManagerException {
+ final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
+ ParsedPackage parsedPackage = request.mParsedPackage;
+ PackageSetting pkgSetting = request.mPkgSetting;
+ final PackageSetting disabledPkgSetting = request.mDisabledPkgSetting;
+ final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
+ final @ParsingPackageUtils.ParseFlags int parseFlags = request.mParseFlags;
+ final @PackageManagerService.ScanFlags int scanFlags = request.mScanFlags;
+ final String realPkgName = request.mRealPkgName;
+ final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
+ final UserHandle user = request.mUser;
+ final boolean isPlatformPackage = request.mIsPlatformPackage;
+
+ List<String> changedAbiCodePath = null;
+
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
+ Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
+ }
+ }
+
+ // Initialize package source and resource directories
+ final File destCodeFile = new File(parsedPackage.getPath());
+
+ // We keep references to the derived CPU Abis from settings in oder to reuse
+ // them in the case where we're not upgrading or booting for the first time.
+ String primaryCpuAbiFromSettings = null;
+ String secondaryCpuAbiFromSettings = null;
+ boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+ if (!needToDeriveAbi) {
+ if (pkgSetting != null) {
+ // TODO(b/154610922): if it is not first boot or upgrade, we should directly use
+ // API info from existing package setting. However, stub packages currently do not
+ // preserve ABI info, thus the special condition check here. Remove the special
+ // check after we fix the stub generation.
+ if (pkgSetting.getPkg() != null && pkgSetting.getPkg().isStub()) {
+ needToDeriveAbi = true;
+ } else {
+ primaryCpuAbiFromSettings = pkgSetting.getPrimaryCpuAbi();
+ secondaryCpuAbiFromSettings = pkgSetting.getSecondaryCpuAbi();
+ }
+ } else {
+ // Re-scanning a system package after uninstalling updates; need to derive ABI
+ needToDeriveAbi = true;
+ }
+ }
+
+ int previousAppId = Process.INVALID_UID;
+
+ if (pkgSetting != null && pkgSetting.getSharedUser() != sharedUserSetting) {
+ if (pkgSetting.getSharedUser() != null && sharedUserSetting == null) {
+ previousAppId = pkgSetting.getAppId();
+ // Log that something is leaving shareduid and keep going
+ Slog.i(TAG,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + pkgSetting.getSharedUser().name + " to " + "<nothing>.");
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + (pkgSetting.getSharedUser() != null
+ ? pkgSetting.getSharedUser().name : "<nothing>")
+ + " to "
+ + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+ + "; replacing with new");
+ pkgSetting = null;
+ }
+ }
+
+ String[] usesSdkLibraries = null;
+ if (!parsedPackage.getUsesSdkLibraries().isEmpty()) {
+ usesSdkLibraries = new String[parsedPackage.getUsesSdkLibraries().size()];
+ parsedPackage.getUsesSdkLibraries().toArray(usesSdkLibraries);
+ }
+
+ String[] usesStaticLibraries = null;
+ if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
+ usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
+ parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
+ }
+
+ final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();
+
+ // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
+ // to avoid adding something that's unsupported due to lack of state, since it's called
+ // with null.
+ final boolean createNewPackage = (pkgSetting == null);
+ if (createNewPackage) {
+ final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
+
+ // Flags contain system values stored in the server variant of AndroidPackage,
+ // and so the server-side PackageInfoUtils is still called, even without a
+ // PackageSetting to pass in.
+ int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);
+ int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);
+
+ // REMOVE SharedUserSetting from method; update in a separate call
+ pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
+ originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
+ destCodeFile, parsedPackage.getNativeLibraryRootDir(),
+ AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
+ AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
+ parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
+ true /*allowInstall*/, instantApp, virtualPreload,
+ UserManagerService.getInstance(), usesSdkLibraries,
+ parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries,
+ parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
+ newDomainSetId);
+ } else {
+ // make a deep copy to avoid modifying any existing system state.
+ pkgSetting = new PackageSetting(pkgSetting);
+ pkgSetting.setPkg(parsedPackage);
+
+ // REMOVE SharedUserSetting from method; update in a separate call.
+ //
+ // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
+ // secondaryCpuAbi are not known at this point so we always update them
+ // to null here, only to reset them at a later point.
+ Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
+ destCodeFile, parsedPackage.getNativeLibraryDir(),
+ AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
+ PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
+ UserManagerService.getInstance(),
+ usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(),
+ usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
+ parsedPackage.getMimeGroups(), newDomainSetId);
+ }
+ if (createNewPackage && originalPkgSetting != null) {
+ // This is the initial transition from the original package, so,
+ // fix up the new package's name now. We must do this after looking
+ // up the package under its new name, so getPackageLP takes care of
+ // fiddling things correctly.
+ parsedPackage.setPackageName(originalPkgSetting.getPackageName());
+
+ // File a report about this.
+ String msg = "New package " + pkgSetting.getRealName()
+ + " renamed to replace old package " + pkgSetting.getPackageName();
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ }
+
+ final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());
+ // for existing packages, change the install state; but, only if it's explicitly specified
+ if (!createNewPackage) {
+ final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
+ setInstantAppForUser(injector, pkgSetting, userId, instantApp, fullApp);
+ }
+ // TODO(patb): see if we can do away with disabled check here.
+ if (disabledPkgSetting != null
+ || (0 != (scanFlags & SCAN_NEW_INSTALL)
+ && pkgSetting != null && pkgSetting.isSystem())) {
+ pkgSetting.getPkgState().setUpdatedSystemApp(true);
+ }
+
+ parsedPackage.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,
+ injector.getCompatibility()));
+
+ if (parsedPackage.isSystem()) {
+ configurePackageComponents(parsedPackage);
+ }
+
+ final String cpuAbiOverride = deriveAbiOverride(request.mCpuAbiOverride);
+ final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
+
+ final File appLib32InstallDir = getAppLib32InstallDir();
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+ if (needToDeriveAbi) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
+ final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
+ packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
+ cpuAbiOverride, appLib32InstallDir);
+ derivedAbi.first.applyTo(parsedPackage);
+ derivedAbi.second.applyTo(parsedPackage);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+ // Some system apps still use directory structure for native libraries
+ // in which case we might end up not detecting abi solely based on apk
+ // structure. Try to detect abi based on directory structure.
+
+ String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
+ if (parsedPackage.isSystem() && !isUpdatedSystemApp
+ && pkgRawPrimaryCpuAbi == null) {
+ final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
+ parsedPackage);
+ abis.applyTo(parsedPackage);
+ abis.applyTo(pkgSetting);
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+ }
+ } else {
+ // This is not a first boot or an upgrade, don't bother deriving the
+ // ABI during the scan. Instead, trust the value that was stored in the
+ // package setting.
+ parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings)
+ .setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
+
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG, "Using ABIS and native lib paths from settings : "
+ + parsedPackage.getPackageName() + " "
+ + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
+ + ", "
+ + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
+ }
+ }
+ } else {
+ if ((scanFlags & SCAN_MOVE) != 0) {
+ // We haven't run dex-opt for this move (since we've moved the compiled output too)
+ // but we already have this packages package info in the PackageSetting. We just
+ // use that and derive the native library path based on the new code path.
+ parsedPackage.setPrimaryCpuAbi(pkgSetting.getPrimaryCpuAbi())
+ .setSecondaryCpuAbi(pkgSetting.getSecondaryCpuAbi());
+ }
+
+ // Set native library paths again. For moves, the path will be updated based on the
+ // ABIs we've determined above. For non-moves, the path will be updated based on the
+ // ABIs we determined during compilation, but the path will depend on the final
+ // package path (after the rename away from the stage path).
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
+ appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+ }
+
+ // This is a special case for the "system" package, where the ABI is
+ // dictated by the zygote configuration (and init.rc). We should keep track
+ // of this ABI so that we can deal with "normal" applications that run under
+ // the same UID correctly.
+ if (isPlatformPackage) {
+ parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit()
+ ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);
+ }
+
+ // If there's a mismatch between the abi-override in the package setting
+ // and the abiOverride specified for the install. Warn about this because we
+ // would've already compiled the app without taking the package setting into
+ // account.
+ if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
+ if (cpuAbiOverride == null) {
+ Slog.w(TAG, "Ignoring persisted ABI override for package "
+ + parsedPackage.getPackageName());
+ }
+ }
+
+ pkgSetting.setPrimaryCpuAbi(AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage))
+ .setSecondaryCpuAbi(AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage))
+ .setCpuAbiOverride(cpuAbiOverride);
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
+ + " to root=" + parsedPackage.getNativeLibraryRootDir()
+ + ", to dir=" + parsedPackage.getNativeLibraryDir()
+ + ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());
+ }
+
+ // Push the derived path down into PackageSettings so we know what to
+ // clean up at uninstall time.
+ pkgSetting.setLegacyNativeLibraryPath(parsedPackage.getNativeLibraryRootDir());
+
+ if (DEBUG_ABI_SELECTION) {
+ Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
+ + " primary=" + pkgSetting.getPrimaryCpuAbi()
+ + " secondary=" + pkgSetting.getSecondaryCpuAbi()
+ + " abiOverride=" + pkgSetting.getCpuAbiOverride());
+ }
+
+ if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.getSharedUser() != null) {
+ // We don't do this here during boot because we can do it all
+ // at once after scanning all existing packages.
+ //
+ // We also do this *before* we perform dexopt on this package, so that
+ // we can avoid redundant dexopts, and also to make sure we've got the
+ // code and package path correct.
+ changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.getSharedUser(),
+ parsedPackage, packageAbiHelper.getAdjustedAbiForSharedUser(
+ pkgSetting.getSharedUser().packages, parsedPackage));
+ }
+
+ parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
+ .contains(android.Manifest.permission.FACTORY_TEST));
+
+ if (parsedPackage.isSystem()) {
+ pkgSetting.setIsOrphaned(true);
+ }
+
+ // Take care of first install / last update times.
+ final long scanFileTime = getLastModifiedTime(parsedPackage);
+ if (currentTime != 0) {
+ if (pkgSetting.getFirstInstallTime() == 0) {
+ pkgSetting.setFirstInstallTime(currentTime)
+ .setLastUpdateTime(currentTime);
+ } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
+ pkgSetting.setLastUpdateTime(currentTime);
+ }
+ } else if (pkgSetting.getFirstInstallTime() == 0) {
+ // We need *something*. Take time time stamp of the file.
+ pkgSetting.setFirstInstallTime(scanFileTime)
+ .setLastUpdateTime(scanFileTime);
+ } else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
+ if (scanFileTime != pkgSetting.getLastModifiedTime()) {
+ // A package on the system image has changed; consider this
+ // to be an update.
+ pkgSetting.setLastUpdateTime(scanFileTime);
+ }
+ }
+ pkgSetting.setLastModifiedTime(scanFileTime);
+ // TODO(b/135203078): Remove, move to constructor
+ pkgSetting.setPkg(parsedPackage)
+ .setFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting))
+ .setPrivateFlags(
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting));
+ if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) {
+ pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode());
+ }
+ // Update volume if needed
+ final String volumeUuid = parsedPackage.getVolumeUuid();
+ if (!Objects.equals(volumeUuid, pkgSetting.getVolumeUuid())) {
+ Slog.i(PackageManagerService.TAG,
+ "Update" + (pkgSetting.isSystem() ? " system" : "")
+ + " package " + parsedPackage.getPackageName()
+ + " volume from " + pkgSetting.getVolumeUuid()
+ + " to " + volumeUuid);
+ pkgSetting.setVolumeUuid(volumeUuid);
+ }
+
+ SharedLibraryInfo sdkLibraryInfo = null;
+ if (!TextUtils.isEmpty(parsedPackage.getSdkLibName())) {
+ sdkLibraryInfo = AndroidPackageUtils.createSharedLibraryForSdk(parsedPackage);
+ }
+ SharedLibraryInfo staticSharedLibraryInfo = null;
+ if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
+ staticSharedLibraryInfo =
+ AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
+ }
+ List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
+ if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
+ dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
+ for (String name : parsedPackage.getLibraryNames()) {
+ dynamicSharedLibraryInfos.add(
+ AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));
+ }
+ }
+
+ return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
+ !createNewPackage /* existingSettingCopied */,
+ previousAppId, sdkLibraryInfo, staticSharedLibraryInfo,
+ dynamicSharedLibraryInfos);
+ }
+
+ /**
+ * Returns the actual scan flags depending upon the state of the other settings.
+ * <p>Updated system applications will not have the following flags set
+ * by default and need to be adjusted after the fact:
+ * <ul>
+ * <li>{@link PackageManagerService.SCAN_AS_SYSTEM}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_PRIVILEGED}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_OEM}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_VENDOR}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_PRODUCT}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_SYSTEM_EXT}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_INSTANT_APP}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_ODM}</li>
+ * </ul>
+ */
+ public static @PackageManagerService.ScanFlags int adjustScanFlagsWithPackageSetting(
+ @PackageManagerService.ScanFlags int scanFlags,
+ PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user) {
+
+ // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
+ // the correct isSystem value now that we don't disable system packages before scan.
+ final PackageSetting systemPkgSetting =
+ (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
+ && pkgSetting != null && pkgSetting.isSystem()
+ ? pkgSetting
+ : disabledPkgSetting;
+ if (systemPkgSetting != null) {
+ // updated system application, must at least have SCAN_AS_SYSTEM
+ scanFlags |= SCAN_AS_SYSTEM;
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
+ scanFlags |= SCAN_AS_OEM;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
+ scanFlags |= SCAN_AS_VENDOR;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
+ scanFlags |= SCAN_AS_PRODUCT;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
+ scanFlags |= SCAN_AS_SYSTEM_EXT;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
+ scanFlags |= SCAN_AS_ODM;
+ }
+ }
+ if (pkgSetting != null) {
+ final int userId = ((user == null) ? 0 : user.getIdentifier());
+ if (pkgSetting.getInstantApp(userId)) {
+ scanFlags |= SCAN_AS_INSTANT_APP;
+ }
+ if (pkgSetting.getVirtualPreload(userId)) {
+ scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
+ }
+ }
+
+ return scanFlags;
+ }
+
+ /**
+ * Enforces code policy for the package. This ensures that if an APK has
+ * declared hasCode="true" in its manifest that the APK actually contains
+ * code.
+ *
+ * @throws PackageManagerException If bytecode could not be found when it should exist
+ */
+ public static void assertCodePolicy(AndroidPackage pkg)
+ throws PackageManagerException {
+ final boolean shouldHaveCode = pkg.isHasCode();
+ if (shouldHaveCode && !apkHasCode(pkg.getBaseApkPath())) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Package " + pkg.getBaseApkPath() + " code is missing");
+ }
+
+ if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
+ for (int i = 0; i < pkg.getSplitCodePaths().length; i++) {
+ final boolean splitShouldHaveCode =
+ (pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ if (splitShouldHaveCode && !apkHasCode(pkg.getSplitCodePaths()[i])) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Package " + pkg.getSplitCodePaths()[i] + " code is missing");
+ }
+ }
+ }
+ }
+
+ public static void assertStaticSharedLibraryIsValid(AndroidPackage pkg,
+ @PackageManagerService.ScanFlags int scanFlags) throws PackageManagerException {
+ // Static shared libraries should have at least O target SDK
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs must target O SDK or higher");
+ }
+
+ // Package declaring static a shared lib cannot be instant apps
+ if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot be instant apps");
+ }
+
+ // Package declaring static a shared lib cannot be renamed since the package
+ // name is synthetic and apps can't code around package manager internals.
+ if (!ArrayUtils.isEmpty(pkg.getOriginalPackages())) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot be renamed");
+ }
+
+ // Package declaring static a shared lib cannot declare dynamic libs
+ if (!ArrayUtils.isEmpty(pkg.getLibraryNames())) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot declare dynamic libs");
+ }
+
+ // Package declaring static a shared lib cannot declare shared users
+ if (pkg.getSharedUserId() != null) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot declare shared users");
+ }
+
+ // Static shared libs cannot declare activities
+ if (!pkg.getActivities().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare activities");
+ }
+
+ // Static shared libs cannot declare services
+ if (!pkg.getServices().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare services");
+ }
+
+ // Static shared libs cannot declare providers
+ if (!pkg.getProviders().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare content providers");
+ }
+
+ // Static shared libs cannot declare receivers
+ if (!pkg.getReceivers().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare broadcast receivers");
+ }
+
+ // Static shared libs cannot declare permission groups
+ if (!pkg.getPermissionGroups().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare permission groups");
+ }
+
+ // Static shared libs cannot declare attributions
+ if (!pkg.getAttributions().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare features");
+ }
+
+ // Static shared libs cannot declare permissions
+ if (!pkg.getPermissions().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare permissions");
+ }
+
+ // Static shared libs cannot declare protected broadcasts
+ if (!pkg.getProtectedBroadcasts().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare protected broadcasts");
+ }
+
+ // Static shared libs cannot be overlay targets
+ if (pkg.getOverlayTarget() != null) {
+ throw new PackageManagerException(
+ "Static shared libs cannot be overlay targets");
+ }
+ }
+
+ public static void assertProcessesAreValid(AndroidPackage pkg) throws PackageManagerException {
+ final Map<String, ParsedProcess> procs = pkg.getProcesses();
+ if (!procs.isEmpty()) {
+ if (!procs.containsKey(pkg.getProcessName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_PROCESS_NOT_DEFINED,
+ "Can't install because application tag's process attribute "
+ + pkg.getProcessName()
+ + " (in package " + pkg.getPackageName()
+ + ") is not included in the <processes> list");
+ }
+ assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
+ assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
+ assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
+ assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
+ }
+ }
+
+ private static <T extends ParsedMainComponent> void assertPackageProcesses(AndroidPackage pkg,
+ List<T> components, Map<String, ParsedProcess> procs, String compName)
+ throws PackageManagerException {
+ if (components == null) {
+ return;
+ }
+ for (int i = components.size() - 1; i >= 0; i--) {
+ final ParsedMainComponent component = components.get(i);
+ if (!procs.containsKey(component.getProcessName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_PROCESS_NOT_DEFINED,
+ "Can't install because " + compName + " " + component.getClassName()
+ + "'s process attribute " + component.getProcessName()
+ + " (in package " + pkg.getPackageName()
+ + ") is not included in the <processes> list");
+ }
+ }
+ }
+
+ public static void assertMinSignatureSchemeIsValid(AndroidPackage pkg,
+ @ParsingPackageUtils.ParseFlags int parseFlags) throws PackageManagerException {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ int minSignatureSchemeVersion =
+ ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
+ pkg.getTargetSdkVersion());
+ if (pkg.getSigningDetails().getSignatureSchemeVersion()
+ < minSignatureSchemeVersion) {
+ throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "No signature found in package of version " + minSignatureSchemeVersion
+ + " or newer for package " + pkg.getPackageName());
+ }
+ }
+ }
+
+ /**
+ * Returns the "real" name of the package.
+ * <p>This may differ from the package's actual name if the application has already
+ * been installed under one of this package's original names.
+ */
+ public static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ if (isPackageRenamed(pkg, renamedPkgName)) {
+ return AndroidPackageUtils.getRealPackageOrNull(pkg);
+ }
+ return null;
+ }
+
+ /** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
+ public static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ return pkg.getOriginalPackages().contains(renamedPkgName);
+ }
+
+ /**
+ * Renames the package if it was installed under a different name.
+ * <p>When we've already installed the package under an original name, update
+ * the new package so we can continue to have the old name.
+ */
+ public static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
+ @NonNull String renamedPackageName) {
+ if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
+ || parsedPackage.getPackageName().equals(renamedPackageName)) {
+ return;
+ }
+ parsedPackage.setPackageName(renamedPackageName);
+ }
+
+ /**
+ * Returns {@code true} if the given file contains code. Otherwise {@code false}.
+ */
+ public static boolean apkHasCode(String fileName) {
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(fileName,
+ false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
+ return jarFile.findEntry("classes.dex") != null;
+ } catch (IOException ignore) {
+ } finally {
+ try {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ } catch (IOException ignore) {
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sets the enabled state of components configured through {@link SystemConfig}.
+ * This modifies the {@link PackageSetting} object.
+ *
+ * TODO(b/135203078): Move this to package parsing
+ **/
+ public static void configurePackageComponents(AndroidPackage pkg) {
+ final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
+ .getComponentsEnabledStates(pkg.getPackageName());
+ if (componentsEnabledStates == null) {
+ return;
+ }
+
+ for (int i = ArrayUtils.size(pkg.getActivities()) - 1; i >= 0; i--) {
+ final ParsedActivity component = pkg.getActivities().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ ComponentMutateUtils.setEnabled(component, enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getReceivers()) - 1; i >= 0; i--) {
+ final ParsedActivity component = pkg.getReceivers().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ ComponentMutateUtils.setEnabled(component, enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getProviders()) - 1; i >= 0; i--) {
+ final ParsedProvider component = pkg.getProviders().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ ComponentMutateUtils.setEnabled(component, enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getServices()) - 1; i >= 0; i--) {
+ final ParsedService component = pkg.getServices().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ ComponentMutateUtils.setEnabled(component, enabled);
+ }
+ }
+ }
+
+ public static int getVendorPartitionVersion() {
+ final String version = SystemProperties.get("ro.vndk.version");
+ if (!version.isEmpty()) {
+ try {
+ return Integer.parseInt(version);
+ } catch (NumberFormatException ignore) {
+ if (ArrayUtils.contains(Build.VERSION.ACTIVE_CODENAMES, version)) {
+ return Build.VERSION_CODES.CUR_DEVELOPMENT;
+ }
+ }
+ }
+ return Build.VERSION_CODES.P;
+ }
+
+ /**
+ * Applies policy to the parsed package based upon the given policy flags.
+ * Ensures the package is in a good state.
+ * <p>
+ * Implementation detail: This method must NOT have any side effect. It would
+ * ideally be static, but, it requires locks to read system state.
+ */
+ public static void applyPolicy(ParsedPackage parsedPackage,
+ final @PackageManagerService.ScanFlags int scanFlags, AndroidPackage platformPkg,
+ boolean isUpdatedSystemApp) {
+ if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
+ parsedPackage.setSystem(true);
+ // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
+ // is set during parse.
+ if (parsedPackage.isDirectBootAware()) {
+ parsedPackage.setAllComponentsDirectBootAware(true);
+ }
+ if (compressedFileExists(parsedPackage.getPath())) {
+ parsedPackage.setStub(true);
+ }
+ } else {
+ parsedPackage
+ // Non system apps cannot mark any broadcast as protected
+ .clearProtectedBroadcasts()
+ // non system apps can't be flagged as core
+ .setCoreApp(false)
+ // clear flags not applicable to regular apps
+ .setPersistent(false)
+ .setDefaultToDeviceProtectedStorage(false)
+ .setDirectBootAware(false)
+ // non system apps can't have permission priority
+ .capPermissionPriorities();
+ }
+ if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
+ parsedPackage
+ .markNotActivitiesAsNotExportedIfSingleUser();
+ }
+
+ parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
+ .setOem((scanFlags & SCAN_AS_OEM) != 0)
+ .setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
+ .setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
+ .setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
+ .setOdm((scanFlags & SCAN_AS_ODM) != 0);
+
+ // Check if the package is signed with the same key as the platform package.
+ parsedPackage.setSignedWithPlatformKey(
+ (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
+ || (platformPkg != null && compareSignatures(
+ platformPkg.getSigningDetails().getSignatures(),
+ parsedPackage.getSigningDetails().getSignatures()
+ ) == PackageManager.SIGNATURE_MATCH))
+ );
+
+ if (!parsedPackage.isSystem()) {
+ // Only system apps can use these features.
+ parsedPackage.clearOriginalPackages()
+ .clearAdoptPermissions();
+ }
+
+ PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
+ }
+
+ /**
+ * Applies the adjusted ABI calculated by
+ * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all
+ * relevant packages and settings.
+ * @param sharedUserSetting The {@code SharedUserSetting} to adjust
+ * @param scannedPackage the package being scanned or null
+ * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}
+ * @return the list of code paths that belong to packages that had their ABIs adjusted.
+ */
+ public static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
+ ParsedPackage scannedPackage, String adjustedAbi) {
+ if (scannedPackage != null) {
+ scannedPackage.setPrimaryCpuAbi(adjustedAbi);
+ }
+ List<String> changedAbiCodePath = null;
+ for (PackageSetting ps : sharedUserSetting.packages) {
+ if (scannedPackage == null
+ || !scannedPackage.getPackageName().equals(ps.getPackageName())) {
+ if (ps.getPrimaryCpuAbi() != null) {
+ continue;
+ }
+
+ ps.setPrimaryCpuAbi(adjustedAbi);
+ if (ps.getPkg() != null) {
+ if (!TextUtils.equals(adjustedAbi,
+ AndroidPackageUtils.getRawPrimaryCpuAbi(ps.getPkg()))) {
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG,
+ "Adjusting ABI for " + ps.getPackageName() + " to "
+ + adjustedAbi + " (scannedPackage="
+ + (scannedPackage != null ? scannedPackage : "null")
+ + ")");
+ }
+ if (changedAbiCodePath == null) {
+ changedAbiCodePath = new ArrayList<>();
+ }
+ changedAbiCodePath.add(ps.getPathString());
+ }
+ }
+ }
+ }
+ return changedAbiCodePath;
+ }
+
+ public static void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
+ Settings.VersionInfo settingsVersionForPackage, boolean forceCollect,
+ boolean skipVerify, boolean isPreNMR1Upgrade)
+ throws PackageManagerException {
+ // When upgrading from pre-N MR1, verify the package time stamp using the package
+ // directory and not the APK file.
+ final long lastModifiedTime = isPreNMR1Upgrade
+ ? new File(parsedPackage.getPath()).lastModified()
+ : getLastModifiedTime(parsedPackage);
+ if (ps != null && !forceCollect
+ && ps.getPathString().equals(parsedPackage.getPath())
+ && ps.getLastModifiedTime() == lastModifiedTime
+ && !ReconcilePackageUtils.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
+ && !ReconcilePackageUtils.isRecoverSignatureUpdateNeeded(
+ settingsVersionForPackage)) {
+ if (ps.getSigningDetails().getSignatures() != null
+ && ps.getSigningDetails().getSignatures().length != 0
+ && ps.getSigningDetails().getSignatureSchemeVersion()
+ != SigningDetails.SignatureSchemeVersion.UNKNOWN) {
+ // Optimization: reuse the existing cached signing data
+ // if the package appears to be unchanged.
+ parsedPackage.setSigningDetails(
+ new SigningDetails(ps.getSigningDetails()));
+ return;
+ }
+
+ Slog.w(TAG, "PackageSetting for " + ps.getPackageName()
+ + " is missing signatures. Collecting certs again to recover them.");
+ } else {
+ Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
+ + (forceCollect ? " (forced)" : ""));
+ }
+
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+ input, parsedPackage, skipVerify);
+ if (result.isError()) {
+ throw new PackageManagerException(
+ result.getErrorCode(), result.getErrorMessage(), result.getException());
+ }
+ parsedPackage.setSigningDetails(result.getResult());
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ public static void setInstantAppForUser(PackageManagerServiceInjector injector,
+ PackageSetting pkgSetting, int userId, boolean instantApp, boolean fullApp) {
+ // no state specified; do nothing
+ if (!instantApp && !fullApp) {
+ return;
+ }
+ if (userId != UserHandle.USER_ALL) {
+ if (instantApp && !pkgSetting.getInstantApp(userId)) {
+ pkgSetting.setInstantApp(true /*instantApp*/, userId);
+ } else if (fullApp && pkgSetting.getInstantApp(userId)) {
+ pkgSetting.setInstantApp(false /*instantApp*/, userId);
+ }
+ } else {
+ for (int currentUserId : injector.getUserManagerInternal().getUserIds()) {
+ if (instantApp && !pkgSetting.getInstantApp(currentUserId)) {
+ pkgSetting.setInstantApp(true /*instantApp*/, currentUserId);
+ } else if (fullApp && pkgSetting.getInstantApp(currentUserId)) {
+ pkgSetting.setInstantApp(false /*instantApp*/, currentUserId);
+ }
+ }
+ }
+ }
+
+ /** Directory where installed application's 32-bit native libraries are copied. */
+ public static File getAppLib32InstallDir() {
+ return new File(Environment.getDataDirectory(), "app-lib");
+ }
+}
diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java
index e1442dd080f5..4334cbdce1f2 100644
--- a/services/core/java/com/android/server/pm/VerificationParams.java
+++ b/services/core/java/com/android/server/pm/VerificationParams.java
@@ -507,6 +507,13 @@ final class VerificationParams extends HandlerParams {
requiredVerifierPackage, verificationTimeout,
verifierUserId, false,
REASON_PACKAGE_VERIFIER, "package verifier");
+
+ if (streaming) {
+ // For streaming installations, count verification timeout from the broadcast.
+ startVerificationTimeoutCountdown(verificationId, streaming, response,
+ verificationTimeout);
+ }
+
mPm.mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
/* appOp= */ AppOpsManager.OP_NONE,
@@ -514,12 +521,12 @@ final class VerificationParams extends HandlerParams {
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- final Message msg = mPm.mHandler
- .obtainMessage(CHECK_PENDING_VERIFICATION);
- msg.arg1 = verificationId;
- msg.arg2 = streaming ? 1 : 0;
- msg.obj = response;
- mPm.mHandler.sendMessageDelayed(msg, verificationTimeout);
+ if (!streaming) {
+ // For NON-streaming installations, count verification timeout from
+ // the broadcast was processed by all receivers.
+ startVerificationTimeoutCountdown(verificationId, streaming, response,
+ verificationTimeout);
+ }
}
}, null, 0, null, null);
@@ -532,6 +539,15 @@ final class VerificationParams extends HandlerParams {
mWaitForVerificationToComplete = true;
}
+ private void startVerificationTimeoutCountdown(int verificationId, boolean streaming,
+ PackageVerificationResponse response, long verificationTimeout) {
+ final Message msg = mPm.mHandler.obtainMessage(CHECK_PENDING_VERIFICATION);
+ msg.arg1 = verificationId;
+ msg.arg2 = streaming ? 1 : 0;
+ msg.obj = response;
+ mPm.mHandler.sendMessageDelayed(msg, verificationTimeout);
+ }
+
/**
* Get the default verification agent response code.
*
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 8643b5f60563..6c1ef2eda41d 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -435,7 +435,8 @@ final class DefaultPermissionGrantPolicy {
|| !pm.isGranted(Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
pkg, UserHandle.of(userId))
|| !pm.isGranted(Manifest.permission.READ_PHONE_STATE, pkg,
- UserHandle.of(userId))) {
+ UserHandle.of(userId))
+ || pm.isSysComponentOrPersistentPlatformSignedPrivApp(pkg)) {
continue;
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index d1b99380b093..c9fd12219562 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -23,6 +23,7 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE;
import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME;
@@ -108,6 +109,7 @@ import android.util.DebugUtils;
import android.util.EventLog;
import android.util.IntArray;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -196,6 +198,9 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
/** All nearby devices permissions */
private static final List<String> NEARBY_DEVICES_PERMISSIONS = new ArrayList<>();
+ // TODO: This is a placeholder. Replace with actual implementation
+ private static final List<String> NOTIFICATION_PERMISSIONS = new ArrayList<>();
+
/**
* All permissions that should be granted with the REVOKE_WHEN_REQUESTED flag, if they are
* implicitly added to a package
@@ -4636,23 +4641,231 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
return true;
}
- private void onPackageInstalledInternal(@NonNull AndroidPackage pkg, int previousAppId,
- @NonNull PermissionManagerServiceInternal.PackageInstalledParams params,
- @UserIdInt int[] userIds) {
- // If previousAppId is not Process.INVALID_UID, the package is performing a migration out
- // of a shared user group. Operations we need to do before calling updatePermissions():
- // - Retrieve the original uid permission state and create a copy of it as the new app's
- // uid state. The new permission state will be properly updated in updatePermissions().
- // - Remove the app from the original shared user group. Other apps in the shared
- // user group will perceive as if the original app is uninstalled.
- if (previousAppId != Process.INVALID_UID) {
- final PackageStateInternal ps =
- mPackageManagerInt.getPackageStateInternal(pkg.getPackageName());
+ private boolean isEffectivelyGranted(PermissionState state) {
+ final int flags = state.getFlags();
+ final int denyMask = FLAG_PERMISSION_REVIEW_REQUIRED
+ | FLAG_PERMISSION_REVOKED_COMPAT
+ | FLAG_PERMISSION_ONE_TIME;
+
+ if ((flags & FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+ return true;
+ } else if ((flags & FLAG_PERMISSION_POLICY_FIXED) != 0) {
+ return (flags & FLAG_PERMISSION_REVOKED_COMPAT) == 0 && state.isGranted();
+ } else if ((flags & denyMask) != 0) {
+ return false;
+ } else {
+ return state.isGranted();
+ }
+ }
+
+ /**
+ * Merge srcState into destState. Return [granted, flags].
+ */
+ private Pair<Boolean, Integer> mergePermissionState(int appId,
+ PermissionState srcState, PermissionState destState) {
+ // This merging logic prioritizes the shared permission state (destState) over
+ // the current package's state (srcState), because an uninstallation of a previously
+ // unrelated app (the updated system app) should not affect the functionality of
+ // existing apps (other apps in the shared UID group).
+
+ final int userSettableMask = FLAG_PERMISSION_USER_SET
+ | FLAG_PERMISSION_USER_FIXED
+ | FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY;
+
+ final int defaultGrantMask = FLAG_PERMISSION_GRANTED_BY_DEFAULT
+ | FLAG_PERMISSION_GRANTED_BY_ROLE;
+
+ final int priorityFixedMask = FLAG_PERMISSION_SYSTEM_FIXED
+ | FLAG_PERMISSION_POLICY_FIXED;
+
+ final int priorityMask = defaultGrantMask | priorityFixedMask;
+
+ final int destFlags = destState.getFlags();
+ final boolean destIsGranted = isEffectivelyGranted(destState);
+
+ final int srcFlags = srcState.getFlags();
+ final boolean srcIsGranted = isEffectivelyGranted(srcState);
+
+ final int combinedFlags = destFlags | srcFlags;
+
+ /* Merge flags */
+
+ int newFlags = 0;
+
+ // Inherit user set flags only from dest as we want to preserve the
+ // user preference of destState, not the one of the current package.
+ newFlags |= (destFlags & userSettableMask);
+
+ // Inherit all exempt flags
+ newFlags |= (combinedFlags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT);
+ // If no exempt flags are set, set APPLY_RESTRICTION
+ if ((newFlags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {
+ newFlags |= FLAG_PERMISSION_APPLY_RESTRICTION;
+ }
+
+ // Inherit all priority flags
+ newFlags |= (combinedFlags & priorityMask);
+
+ // If no priority flags are set, inherit REVOKE_WHEN_REQUESTED
+ if ((combinedFlags & priorityMask) == 0) {
+ newFlags |= (combinedFlags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
+ }
+
+ // Handle REVIEW_REQUIRED
+ if ((newFlags & priorityFixedMask) == 0) {
+ if (NOTIFICATION_PERMISSIONS.contains(srcState.getName())) {
+ // For notification permissions, inherit from both states
+ // if no priority FIXED flags are set
+ newFlags |= (combinedFlags & FLAG_PERMISSION_REVIEW_REQUIRED);
+ } else if ((newFlags & priorityMask) == 0) {
+ // Else inherit from destState if no priority flags are set
+ newFlags |= (destFlags & FLAG_PERMISSION_REVIEW_REQUIRED);
+ }
+ }
+
+ /* Determine effective grant state */
+
+ final boolean effectivelyGranted;
+ if ((newFlags & FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+ effectivelyGranted = true;
+ } else if ((destFlags & FLAG_PERMISSION_POLICY_FIXED) != 0) {
+ // If this flag comes from destState, preserve its state
+ effectivelyGranted = destIsGranted;
+ } else if ((srcFlags & FLAG_PERMISSION_POLICY_FIXED) != 0) {
+ effectivelyGranted = destIsGranted || srcIsGranted;
+ // If this flag comes from srcState, preserve flag only if
+ // there is no conflict
+ if (destIsGranted != srcIsGranted) {
+ newFlags &= ~FLAG_PERMISSION_POLICY_FIXED;
+ }
+ } else if ((destFlags & defaultGrantMask) != 0) {
+ // If a permission state has default grant flags and is not
+ // granted, this meant user has overridden the grant state.
+ // Respect the user's preference on destState.
+ // Due to this reason, if this flag comes from destState,
+ // preserve its state
+ effectivelyGranted = destIsGranted;
+ } else if ((srcFlags & defaultGrantMask) != 0) {
+ effectivelyGranted = destIsGranted || srcIsGranted;
+ } else if ((destFlags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
+ // Similar reason to defaultGrantMask, if this flag comes
+ // from destState, preserve its state
+ effectivelyGranted = destIsGranted;
+ } else if ((srcFlags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
+ effectivelyGranted = destIsGranted || srcIsGranted;
+ // If this flag comes from srcState, remove this flag if
+ // destState is already granted to prevent revocation.
+ if (destIsGranted) {
+ newFlags &= ~FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+ }
+ } else {
+ // If still not determined, fallback to destState.
+ effectivelyGranted = destIsGranted;
+ }
+
+ /* Post-processing / fix ups */
+
+ if (!effectivelyGranted) {
+ // If not effectively granted, inherit AUTO_REVOKED
+ newFlags |= (combinedFlags & FLAG_PERMISSION_AUTO_REVOKED);
+
+ // REVOKE_WHEN_REQUESTED make no sense when denied
+ newFlags &= ~FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+ } else {
+ // REVIEW_REQUIRED make no sense when granted
+ newFlags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
+ }
+
+ if (effectivelyGranted != destIsGranted) {
+ // Remove user set flags if state changes
+ newFlags &= ~userSettableMask;
+ }
+
+ // Fix permission state based on targetSdk of the shared UID
+ final boolean newGrantState;
+ if (!effectivelyGranted && isPermissionSplitFromNonRuntime(
+ srcState.getName(),
+ mPackageManagerInt.getUidTargetSdkVersion(appId))) {
+ // Even though effectively denied, it has to be set to granted
+ // for backwards compatibility
+ newFlags |= FLAG_PERMISSION_REVOKED_COMPAT;
+ newGrantState = true;
+ } else {
+ // Either it's effectively granted, or it targets a high enough API level
+ // to handle this permission properly
+ newGrantState = effectivelyGranted;
+ }
+
+ return new Pair<>(newGrantState, newFlags);
+ }
+
+ /**
+ * This method handles permission migration of packages leaving/joining shared UID
+ */
+ private void handleAppIdMigration(@NonNull AndroidPackage pkg, int previousAppId) {
+ final PackageStateInternal ps =
+ mPackageManagerInt.getPackageStateInternal(pkg.getPackageName());
+
+ if (ps.getSharedUser() != null) {
+ // The package is joining a shared user group. This can only happen when a system
+ // app left shared UID with an update, and then the update is uninstalled.
+ // If no apps remain in its original shared UID group, clone the current
+ // permission state to the shared appId; or else, merge the current permission
+ // state into the shared UID state.
+
+ synchronized (mLock) {
+ for (final int userId : getAllUserIds()) {
+ final UserPermissionState userState = mState.getOrCreateUserState(userId);
+
+ // This is the permission state the package was using
+ final UidPermissionState uidState = userState.getUidState(previousAppId);
+ if (uidState == null) {
+ continue;
+ }
+
+ // This is the shared UID permission state the package wants to join
+ final UidPermissionState sharedUidState = userState.getUidState(ps.getAppId());
+ if (sharedUidState == null) {
+ // No apps remain in the shared UID group, clone permissions
+ userState.createUidStateWithExisting(ps.getAppId(), uidState);
+ } else {
+ final List<PermissionState> states = uidState.getPermissionStates();
+ final int count = states.size();
+ for (int i = 0; i < count; ++i) {
+ final PermissionState srcState = states.get(i);
+ final PermissionState destState =
+ sharedUidState.getPermissionState(srcState.getName());
+ if (destState != null) {
+ // Merge the 2 permission states
+ Pair<Boolean, Integer> newState =
+ mergePermissionState(ps.getAppId(), srcState, destState);
+ sharedUidState.putPermissionState(srcState.getPermission(),
+ newState.first, newState.second);
+ } else {
+ // Simply copy the permission state over
+ sharedUidState.putPermissionState(srcState.getPermission(),
+ srcState.isGranted(), srcState.getFlags());
+ }
+ }
+ }
+
+ // Remove permissions for the previous appId
+ userState.removeUidState(previousAppId);
+ }
+ }
+ } else {
+ // The package is migrating out of a shared user group.
+ // Operations we need to do before calling updatePermissions():
+ // - Retrieve the original uid permission state and create a copy of it as the
+ // new app's uid state. The new permission state will be properly updated in
+ // updatePermissions().
+ // - Remove the app from the original shared user group. Other apps in the shared
+ // user group will perceive as if the original app is uninstalled.
+
final List<AndroidPackage> origSharedUserPackages =
mPackageManagerInt.getPackagesForAppId(previousAppId);
synchronized (mLock) {
- // All users are affected
for (final int userId : getAllUserIds()) {
// Retrieve the original uid state
final UserPermissionState userState = mState.getUserState(userId);
@@ -4679,6 +4892,14 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
}
}
+ }
+
+ private void onPackageInstalledInternal(@NonNull AndroidPackage pkg, int previousAppId,
+ @NonNull PermissionManagerServiceInternal.PackageInstalledParams params,
+ @UserIdInt int[] userIds) {
+ if (previousAppId != Process.INVALID_UID) {
+ handleAppIdMigration(pkg, previousAppId);
+ }
updatePermissions(pkg.getPackageName(), pkg);
for (final int userId : userIds) {
addAllowlistedRestrictedPermissionsInternal(pkg,
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index 873d6885f9e1..d70f97095816 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -1549,6 +1549,25 @@ public class TvIAppManagerService extends SystemService {
}
@Override
+ public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType,
+ Bundle parameters) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
+ + parameters.toString() + ")");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onCommandRequest(cmdType, parameters, mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onCommandRequest", e);
+ }
+ }
+ }
+
+ @Override
public void onSessionStateChanged(int state) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index b17257a9db80..344270427569 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -117,7 +117,6 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
PackageManagerInternal mPmInternal;
/** File storing persisted {@link #mGrantedUriPermissions}. */
- @GuardedBy("mLock")
private final AtomicFile mGrantFile;
/** XML constants used in {@link #mGrantFile} */
@@ -1299,15 +1298,14 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
return false;
}
- @GuardedBy("mLock")
- private void writeGrantedUriPermissionsLocked() {
+ private void writeGrantedUriPermissions() {
if (DEBUG) Slog.v(TAG, "writeGrantedUriPermissions()");
final long startTime = SystemClock.uptimeMillis();
// Snapshot permissions so we can persist without lock
ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList();
- synchronized (this) {
+ synchronized (mLock) {
final int size = mGrantedUriPermissions.size();
for (int i = 0; i < size; i++) {
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
@@ -1330,8 +1328,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
out.startTag(null, TAG_URI_GRANT);
out.attributeInt(null, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
out.attributeInt(null, ATTR_TARGET_USER_ID, perm.targetUserId);
- out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
- out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
+ out.attributeInterned(null, ATTR_SOURCE_PKG, perm.sourcePkg);
+ out.attributeInterned(null, ATTR_TARGET_PKG, perm.targetPkg);
out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri));
writeBooleanAttribute(out, ATTR_PREFIX, perm.uri.prefix);
out.attributeInt(null, ATTR_MODE_FLAGS, perm.persistedModeFlags);
@@ -1360,9 +1358,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
public void handleMessage(Message msg) {
switch (msg.what) {
case PERSIST_URI_GRANTS_MSG: {
- synchronized (mLock) {
- writeGrantedUriPermissionsLocked();
- }
+ writeGrantedUriPermissions();
break;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 6c9f1e5bee61..7164c6c601ef 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -49,6 +49,7 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityClientController;
+import android.app.ICompatCameraControlCallback;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.app.PictureInPictureUiState;
@@ -766,6 +767,22 @@ class ActivityClientController extends IActivityClientController.Stub {
Binder.restoreCallingIdentity(origId);
}
+ @Override
+ public void requestCompatCameraControl(IBinder token, boolean showControl,
+ boolean transformationApplied, ICompatCameraControlCallback callback) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
+ if (r != null) {
+ r.updateCameraCompatState(showControl, transformationApplied, callback);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
/**
* Checks the state of the system and the activity associated with the given {@param token} to
* verify that picture-in-picture is supported for that activity.
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index d736ede7e97e..30cd3c4ab8bf 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.TaskInfo;
@@ -33,12 +34,13 @@ import java.lang.annotation.RetentionPolicy;
public abstract class ActivityInterceptorCallback {
/**
* Intercept the launch intent based on various signals. If an interception happened, returns
- * a new/existing non-null {@link Intent} which may redirect to another activity.
+ * a new/existing non-null {@link ActivityInterceptResult} which may redirect to another
+ * activity or with new {@link ActivityOptions}.
*
- * @return null if no interception occurred, or a non-null intent which replaces the
- * existing intent.
+ * @return null if no interception occurred, or a non-null result which replaces the existing
+ * intent and activity options.
*/
- public abstract @Nullable Intent intercept(ActivityInterceptorInfo info);
+ public abstract @Nullable ActivityInterceptResult intercept(ActivityInterceptorInfo info);
/**
* Called when an activity is successfully launched.
@@ -108,4 +110,19 @@ public abstract class ActivityInterceptorCallback {
this.checkedOptions = checkedOptions;
}
}
+
+ /**
+ * Data class for storing the intercept result.
+ */
+ public static final class ActivityInterceptResult {
+ @NonNull public final Intent intent;
+ @NonNull public final ActivityOptions activityOptions;
+
+ public ActivityInterceptResult(
+ @NonNull Intent intent,
+ @NonNull ActivityOptions activityOptions) {
+ this.intent = intent;
+ this.activityOptions = activityOptions;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 447f4bed3300..cde5273b38dd 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -232,9 +232,12 @@ import android.annotation.Size;
import android.app.Activity;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
+import android.app.ICompatCameraControlCallback;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.app.WaitResult;
import android.app.WindowConfiguration;
import android.app.servertransaction.ActivityConfigurationChangeItem;
@@ -712,6 +715,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Nullable
private Rect mLetterboxBoundsForFixedOrientationAndAspectRatio;
+ // State of the Camera app compat control which is used to correct stretched viewfinder
+ // in apps that don't handle all possible configurations and changes between them correctly.
+ @CameraCompatControlState
+ private int mCameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+
+
+ // The callback that allows to ask the calling View to apply the treatment for stretched
+ // issues affecting camera viewfinders when the user clicks on the camera compat control.
+ @Nullable
+ private ICompatCameraControlCallback mCompatCameraControlCallback;
+
+ private final boolean mCameraCompatControlEnabled;
+ private boolean mCameraCompatControlClickedByUser;
+
// activity is not displayed?
// TODO: rename to mNoDisplay
@VisibleForTesting
@@ -1167,6 +1184,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
mLetterboxUiController.dump(pw, prefix);
+
+ pw.println(prefix + "mCameraCompatControlState="
+ + TaskInfo.cameraCompatControlStateToString(mCameraCompatControlState));
+ pw.println(prefix + "mCameraCompatControlEnabled=" + mCameraCompatControlEnabled);
}
static boolean dumpActivity(FileDescriptor fd, PrintWriter pw, int index, ActivityRecord r,
@@ -1572,6 +1593,91 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mLetterboxUiController.getLetterboxInnerBounds(outBounds);
}
+ void updateCameraCompatState(boolean showControl, boolean transformationApplied,
+ ICompatCameraControlCallback callback) {
+ if (!isCameraCompatControlEnabled()) {
+ // Feature is disabled by config_isCameraCompatControlForStretchedIssuesEnabled.
+ return;
+ }
+ if (mCameraCompatControlClickedByUser && (showControl
+ || mCameraCompatControlState == TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED)) {
+ // The user already applied treatment on this activity or dismissed control.
+ // Respecting their choice.
+ return;
+ }
+ mCompatCameraControlCallback = callback;
+ int newCameraCompatControlState = !showControl
+ ? TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN
+ : transformationApplied
+ ? TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
+ : TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ boolean changed = setCameraCompatControlState(newCameraCompatControlState);
+ if (!changed) {
+ return;
+ }
+ if (newCameraCompatControlState == TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN) {
+ mCameraCompatControlClickedByUser = false;
+ mCompatCameraControlCallback = null;
+ }
+ // Trigger TaskInfoChanged to update the camera compat UI.
+ getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
+ }
+
+ void updateCameraCompatStateFromUser(@CameraCompatControlState int state) {
+ if (!isCameraCompatControlEnabled()) {
+ // Feature is disabled by config_isCameraCompatControlForStretchedIssuesEnabled.
+ return;
+ }
+ if (state == TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN) {
+ Slog.w(TAG, "Unexpected hidden state in updateCameraCompatState");
+ return;
+ }
+ boolean changed = setCameraCompatControlState(state);
+ mCameraCompatControlClickedByUser = true;
+ if (!changed) {
+ return;
+ }
+ if (state == TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED) {
+ mCompatCameraControlCallback = null;
+ return;
+ }
+ if (mCompatCameraControlCallback == null) {
+ Slog.w(TAG, "Callback for a camera compat control is null");
+ return;
+ }
+ try {
+ if (state == TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED) {
+ mCompatCameraControlCallback.applyCameraCompatTreatment();
+ } else {
+ mCompatCameraControlCallback.revertCameraCompatTreatment();
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to apply or revert camera compat treatment", e);
+ }
+ }
+
+ private boolean setCameraCompatControlState(@CameraCompatControlState int state) {
+ if (!isCameraCompatControlEnabled()) {
+ // Feature is disabled by config_isCameraCompatControlForStretchedIssuesEnabled.
+ return false;
+ }
+ if (mCameraCompatControlState != state) {
+ mCameraCompatControlState = state;
+ return true;
+ }
+ return false;
+ }
+
+ @CameraCompatControlState
+ int getCameraCompatControlState() {
+ return mCameraCompatControlState;
+ }
+
+ @VisibleForTesting
+ boolean isCameraCompatControlEnabled() {
+ return mCameraCompatControlEnabled;
+ }
+
/**
* @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
* when the current activity is displayed.
@@ -1794,6 +1900,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
taskDescription = _taskDescription;
mLetterboxUiController = new LetterboxUiController(mWmService, this);
+ mCameraCompatControlEnabled = mWmService.mContext.getResources()
+ .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
if (_createTime > 0) {
createTime = _createTime;
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 352a0709d0d2..658a17bd9ec7 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -61,6 +61,7 @@ import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
+import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult;
/**
* A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked}
@@ -194,11 +195,12 @@ class ActivityStartInterceptor {
for (int i = 0; i < callbacks.size(); i++) {
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
- final Intent newIntent = callback.intercept(interceptorInfo);
- if (newIntent == null) {
+ final ActivityInterceptResult interceptResult = callback.intercept(interceptorInfo);
+ if (interceptResult == null) {
continue;
}
- mIntent = newIntent;
+ mIntent = interceptResult.intent;
+ mActivityOptions = interceptResult.activityOptions;
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
mRInfo = mSupervisor.resolveIntent(mIntent, null, mUserId, 0, mRealCallingUid);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 74165647aed2..e80a9b9f2a8e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1843,6 +1843,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
+ /** Returns {@code true} if the decided new rotation has not applied to configuration yet. */
+ private boolean isRotationChanging() {
+ return mDisplayRotation.getRotation() != getWindowConfiguration().getRotation();
+ }
+
+ private void startFadeRotationAnimationIfNeeded() {
+ if (isRotationChanging()) {
+ startFadeRotationAnimation(false /* shouldDebounce */);
+ }
+ }
+
/**
* Starts the hide animation for the windows which will be rotated seamlessly.
*
@@ -1996,23 +2007,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
void configureDisplayPolicy() {
- final int width = mBaseDisplayWidth;
- final int height = mBaseDisplayHeight;
- final int shortSize;
- final int longSize;
- if (width > height) {
- shortSize = height;
- longSize = width;
- } else {
- shortSize = width;
- longSize = height;
- }
-
- final int shortSizeDp = shortSize * DENSITY_DEFAULT / mBaseDisplayDensity;
- final int longSizeDp = longSize * DENSITY_DEFAULT / mBaseDisplayDensity;
-
mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();
- mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
+ mDisplayRotation.configure(mBaseDisplayWidth, mBaseDisplayHeight);
}
/**
@@ -2795,6 +2791,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mIsSizeForced ? mBaseDisplayHeight : newHeight,
mIsDensityForced ? mBaseDisplayDensity : newDensity);
+ configureDisplayPolicy();
+
+ if (physicalDisplayChanged) {
+ // Reapply the rotation window settings, we are doing this after updating
+ // the screen size and configuring display policy as the rotation depends
+ // on the display size
+ mWmService.mDisplayWindowSettings.applyRotationSettingsToDisplayLocked(this);
+ }
+
// Real display metrics changed, so we should also update initial values.
mInitialDisplayWidth = newWidth;
mInitialDisplayHeight = newHeight;
@@ -2829,8 +2834,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mBaseDisplayDensity = baseDensity;
if (mMaxUiWidth > 0 && mBaseDisplayWidth > mMaxUiWidth) {
- mBaseDisplayHeight = (mMaxUiWidth * mBaseDisplayHeight) / mBaseDisplayWidth;
+ final float ratio = mMaxUiWidth / (float) mBaseDisplayWidth;
+ mBaseDisplayHeight = (int) (mBaseDisplayHeight * ratio);
mBaseDisplayWidth = mMaxUiWidth;
+ if (!mIsDensityForced) {
+ // Update the density proportionally so the size of the UI elements won't change
+ // from the user's perspective.
+ mBaseDisplayDensity = (int) (mBaseDisplayDensity * ratio);
+ }
if (DEBUG_DISPLAY) {
Slog.v(TAG_WM, "Applying config restraints:" + mBaseDisplayWidth + "x"
@@ -2887,6 +2898,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** If the given width and height equal to initial size, the setting will be cleared. */
void setForcedSize(int width, int height) {
+ // Can't force size higher than the maximal allowed
+ if (mMaxUiWidth > 0 && width > mMaxUiWidth) {
+ final float ratio = mMaxUiWidth / (float) width;
+ height = (int) (height * ratio);
+ width = mMaxUiWidth;
+ }
+
mIsSizeForced = mInitialDisplayWidth != width || mInitialDisplayHeight != height;
if (mIsSizeForced) {
// Set some sort of reasonable bounds on the size of the display that we will try
@@ -3189,11 +3207,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Hide the windows which are not significant in rotation animation. So that the windows
// don't need to block the unfreeze time.
- if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()
- // Do not fade for freezing without rotation change.
- && mDisplayRotation.getRotation() != getWindowConfiguration().getRotation()
- && mFadeRotationAnimationController == null) {
- startFadeRotationAnimation(false /* shouldDebounce */);
+ if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) {
+ startFadeRotationAnimationIfNeeded();
}
}
@@ -3214,6 +3229,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
if (!controller.isCollecting(this)) {
controller.collect(this);
+ startFadeRotationAnimationIfNeeded();
}
return;
}
@@ -3221,7 +3237,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
this, this, null /* remoteTransition */, displayChange);
if (t != null) {
mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
- if (getRotation() != getWindowConfiguration().getRotation()) {
+ if (isRotationChanging()) {
mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
controller.mTransitionMetricsReporter.associate(t,
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index ae0600790e65..8c8b33f344fd 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -294,7 +294,7 @@ public class DisplayRotation {
currentUserRes.getBoolean(R.bool.config_allowSeamlessRotationDespiteNavBarMoving);
}
- void configure(int width, int height, int shortSizeDp, int longSizeDp) {
+ void configure(int width, int height) {
final Resources res = mContext.getResources();
if (width > height) {
mLandscapeRotation = Surface.ROTATION_0;
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 6d5abe1e2f31..8260fd6c09f4 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -265,10 +265,6 @@ class DisplayWindowSettings {
dc.mIsDensityForced = hasDensityOverride;
dc.mIsSizeForced = hasSizeOverride;
- final boolean ignoreOrientationRequest = settings.mIgnoreOrientationRequest != null
- ? settings.mIgnoreOrientationRequest : false;
- dc.setIgnoreOrientationRequest(ignoreOrientationRequest);
-
final boolean ignoreDisplayCutout = settings.mIgnoreDisplayCutout != null
? settings.mIgnoreDisplayCutout : false;
dc.mIgnoreDisplayCutout = ignoreDisplayCutout;
@@ -288,6 +284,15 @@ class DisplayWindowSettings {
dc.mDontMoveToTop = dontMoveToTop;
}
+ void applyRotationSettingsToDisplayLocked(DisplayContent dc) {
+ final DisplayInfo displayInfo = dc.getDisplayInfo();
+ final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
+
+ final boolean ignoreOrientationRequest = settings.mIgnoreOrientationRequest != null
+ ? settings.mIgnoreOrientationRequest : false;
+ dc.setIgnoreOrientationRequest(ignoreOrientationRequest);
+ }
+
/**
* Updates settings for the given display after system features are loaded into window manager
* service, e.g. if this device is PC and if this device supports freeform.
diff --git a/services/core/java/com/android/server/wm/FadeAnimationController.java b/services/core/java/com/android/server/wm/FadeAnimationController.java
index 817b27a55c1b..561a07061bb4 100644
--- a/services/core/java/com/android/server/wm/FadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeAnimationController.java
@@ -21,7 +21,6 @@ import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
import android.annotation.NonNull;
import android.content.Context;
-import android.util.ArrayMap;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.view.animation.Animation;
@@ -38,7 +37,6 @@ import java.io.PrintWriter;
public class FadeAnimationController {
protected final DisplayContent mDisplayContent;
protected final Context mContext;
- protected final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>();
public FadeAnimationController(DisplayContent displayContent) {
mDisplayContent = displayContent;
@@ -78,16 +76,8 @@ public class FadeAnimationController {
return;
}
- // We deferred the end of the animation when hiding the token, so we need to end it now that
- // it's shown again.
- final SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> {
- final Runnable runnable = mDeferredFinishCallbacks.remove(windowToken);
- if (runnable != null) {
- runnable.run();
- }
- } : null;
windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter,
- show /* hidden */, animationType, finishedCallback);
+ show /* hidden */, animationType, null /* finishedCallback */);
}
protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec,
@@ -135,7 +125,7 @@ public class FadeAnimationController {
};
}
- protected class FadeAnimationAdapter extends LocalAnimationAdapter {
+ protected static class FadeAnimationAdapter extends LocalAnimationAdapter {
protected final boolean mShow;
protected final WindowToken mToken;
@@ -149,13 +139,10 @@ public class FadeAnimationController {
@Override
public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
- // We defer the end of the hide animation to ensure the tokens stay hidden until
- // we show them again.
- if (!mShow) {
- mDeferredFinishCallbacks.put(mToken, endDeferFinishCallback);
- return true;
- }
- return false;
+ // Defer the finish callback (restore leash) of the hide animation to ensure the token
+ // stay hidden until it needs to show again. Besides, when starting the show animation,
+ // the previous hide animation will be cancelled, so the callback can be ignored.
+ return !mShow;
}
}
}
diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
index cf36c85ebabf..bbda577e9c57 100644
--- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
@@ -50,7 +50,7 @@ public class FadeRotationAnimationController extends FadeAnimationController {
/** Whether to use constant zero alpha animation. */
private boolean mHideImmediately;
- /** Whether this controller is triggered from shell transition. */
+ /** Whether this controller is triggered from shell transition with type CHANGE. */
private final boolean mIsChangeTransition;
/** Whether the start transaction of the transition is committed (by shell). */
@@ -59,21 +59,30 @@ public class FadeRotationAnimationController extends FadeAnimationController {
/** The list to store the drawn tokens before the rotation animation starts. */
private ArrayList<WindowToken> mPendingShowTokens;
+ /** It is used when the display has rotated, but some windows fade out in old rotation. */
+ private SeamlessRotator mRotator;
+
+ private final int mOriginalRotation;
+ private final boolean mHasScreenRotationAnimation;
+
public FadeRotationAnimationController(DisplayContent displayContent) {
super(displayContent);
mService = displayContent.mWmService;
- mIsChangeTransition = displayContent.inTransition()
- && displayContent.mTransitionController.getCollectingTransitionType()
- == WindowManager.TRANSIT_CHANGE;
+ mOriginalRotation = displayContent.getWindowConfiguration().getRotation();
+ final int transitionType =
+ displayContent.mTransitionController.getCollectingTransitionType();
+ mIsChangeTransition = transitionType == WindowManager.TRANSIT_CHANGE;
+ // Only CHANGE type (rotation animation) needs to wait for the start transaction.
mIsStartTransactionCommitted = !mIsChangeTransition;
- mTimeoutRunnable = displayContent.getRotationAnimation() != null
- || mIsChangeTransition ? () -> {
+ mTimeoutRunnable = displayContent.inTransition() ? () -> {
synchronized (mService.mGlobalLock) {
displayContent.finishFadeRotationAnimationIfPossible();
mService.mWindowPlacerLocked.performSurfacePlacement();
}
} : null;
- if (mTimeoutRunnable != null) {
+ mHasScreenRotationAnimation =
+ displayContent.getRotationAnimation() != null || mIsChangeTransition;
+ if (mHasScreenRotationAnimation) {
// Hide the windows immediately because screen should have been covered by screenshot.
mHideImmediately = true;
}
@@ -103,6 +112,19 @@ public class FadeRotationAnimationController extends FadeAnimationController {
}, true /* traverseTopToBottom */);
}
+ @Override
+ public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
+ if (show) {
+ final SurfaceControl leash = mTargetWindowTokens.remove(windowToken);
+ if (leash != null && mRotator != null) {
+ // The leash was unrotated by start transaction of transition. Clear the transform
+ // to reshow the window in current rotation.
+ mRotator.setIdentityMatrix(mDisplayContent.getPendingTransaction(), leash);
+ }
+ }
+ super.fadeWindowToken(show, windowToken, animationType);
+ }
+
/** Applies show animation on the previously hidden window tokens. */
void show() {
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
@@ -125,19 +147,23 @@ public class FadeRotationAnimationController extends FadeAnimationController {
* controller is created for normal rotation.
*/
boolean show(WindowToken token) {
+ if (!isTargetToken(token)) return false;
if (!mIsStartTransactionCommitted) {
// The fade-in animation should only start after the screenshot layer is shown by shell.
// Otherwise the window will be blinking before the rotation animation starts. So store
// to a pending list and animate them until the transaction is committed.
- if (mTargetWindowTokens.containsKey(token)) {
- if (mPendingShowTokens == null) {
- mPendingShowTokens = new ArrayList<>();
- }
- mPendingShowTokens.add(token);
+ if (mPendingShowTokens == null) {
+ mPendingShowTokens = new ArrayList<>();
}
+ mPendingShowTokens.add(token);
+ return false;
+ }
+ if (!mHasScreenRotationAnimation && token.mTransitionController.inTransition()) {
+ // Defer showing to onTransitionFinished().
return false;
}
- if (mTimeoutRunnable != null && mTargetWindowTokens.remove(token) != null) {
+ // If the timeout runnable is null (fixed rotation), the case will be handled by show().
+ if (mTimeoutRunnable != null) {
fadeWindowToken(true /* show */, token, ANIMATION_TYPE_FIXED_TRANSFORM);
if (mTargetWindowTokens.isEmpty()) {
mService.mH.removeCallbacks(mTimeoutRunnable);
@@ -177,6 +203,15 @@ public class FadeRotationAnimationController extends FadeAnimationController {
return mTargetWindowTokens.containsKey(token);
}
+ /**
+ * Whether the insets animation leash should use previous position when running fade out
+ * animation in rotated display.
+ */
+ boolean shouldFreezeInsetsPosition(WindowState w) {
+ return !mHasScreenRotationAnimation && w.mTransitionController.inTransition()
+ && isTargetToken(w.mToken);
+ }
+
void setOnShowRunnable(Runnable onShowRunnable) {
mOnShowRunnable = onShowRunnable;
}
@@ -186,6 +221,22 @@ public class FadeRotationAnimationController extends FadeAnimationController {
* transition starts. And associate transaction callback to consume pending animations.
*/
void setupStartTransaction(SurfaceControl.Transaction t) {
+ if (!mIsChangeTransition) {
+ // Take OPEN/CLOSE transition type as the example, the non-activity windows need to
+ // fade out in previous rotation while display has rotated to the new rotation, so
+ // their leashes are unrotated with the start transaction.
+ mRotator = new SeamlessRotator(mOriginalRotation,
+ mDisplayContent.getWindowConfiguration().getRotation(),
+ mDisplayContent.getDisplayInfo(),
+ false /* applyFixedTransformationHint */);
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
+ if (leash != null) {
+ mRotator.applyTransform(t, leash);
+ }
+ }
+ return;
+ }
// Hide the windows immediately because a screenshot layer should cover the screen.
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
@@ -208,9 +259,30 @@ public class FadeRotationAnimationController extends FadeAnimationController {
});
}
+ void onTransitionFinished() {
+ if (mIsChangeTransition) {
+ // With screen rotation animation, the windows are always faded in when they are drawn.
+ // Because if they are drawn fast enough, the fade animation should not be observable.
+ return;
+ }
+ // For other transition types, the fade-in animation runs after the transition to make the
+ // transition animation (e.g. launch activity) look cleaner.
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ final WindowToken token = mTargetWindowTokens.keyAt(i);
+ for (int j = token.getChildCount() - 1; j >= 0; j--) {
+ // Only fade in the drawn windows. If the remaining windows are drawn later,
+ // show(WindowToken) will be called to fade in them.
+ if (token.getChildAt(j).isDrawFinishedLw()) {
+ mDisplayContent.finishFadeRotationAnimation(token);
+ break;
+ }
+ }
+ }
+ }
+
@Override
public Animation getFadeInAnimation() {
- if (mTimeoutRunnable != null) {
+ if (mHasScreenRotationAnimation) {
// Use a shorter animation so it is easier to align with screen rotation animation.
return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter);
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index af917264698e..a8a923140a41 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -297,6 +297,14 @@ class InsetsSourceProvider {
}
private Point getWindowFrameSurfacePosition() {
+ if (mControl != null) {
+ final FadeRotationAnimationController fadeController =
+ mWin.mDisplayContent.getFadeRotationAnimationController();
+ if (fadeController != null && fadeController.shouldFreezeInsetsPosition(mWin)) {
+ // Use previous position because the fade-out animation runs in old rotation.
+ return mControl.getSurfacePosition();
+ }
+ }
final Rect frame = mWin.getFrame();
final Point position = new Point();
mWin.transformFrameToSurfacePosition(frame.left, frame.top, position);
diff --git a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
index af8293aab977..80f2ab62f120 100644
--- a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
@@ -95,14 +95,6 @@ public class NavBarFadeAnimationController extends FadeAnimationController{
} else {
fadeAnim.run();
}
- } else {
- // If fade rotation animation is running and controlling the nav bar, make sure we empty
- // the mDeferredFinishCallbacks and defer the runnable until fade rotation animation
- // finishes.
- final Runnable runnable = mDeferredFinishCallbacks.remove(mNavigationBar.mToken);
- if (runnable != null) {
- controller.setOnShowRunnable(runnable);
- }
}
}
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 9ad30da1cb54..1da0fe731709 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -243,7 +243,8 @@ class PinnedTaskController {
int oldRotation, int newRotation) {
final Rect bounds = mDestRotatedBounds;
final PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
- if (bounds == null && pipTx == null) {
+ final boolean emptyPipPositionTx = pipTx == null || pipTx.mPosition == null;
+ if (bounds == null && emptyPipPositionTx) {
return;
}
final TaskDisplayArea taskArea = mDisplayContent.getDefaultTaskDisplayArea();
@@ -255,7 +256,7 @@ class PinnedTaskController {
mDestRotatedBounds = null;
mPipTransaction = null;
final Rect areaBounds = taskArea.getBounds();
- if (pipTx != null && pipTx.mPosition != null) {
+ if (!emptyPipPositionTx) {
// The transaction from recents animation is in old rotation. So the position needs to
// be rotated.
float dx = pipTx.mPosition.x;
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 4cc369f0a187..c20b85858c44 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -20,7 +20,6 @@ import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.graphics.Matrix;
-import android.os.IBinder;
import android.view.DisplayInfo;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
@@ -73,7 +72,7 @@ public class SeamlessRotator {
* global display rotation.
*/
public void unrotate(Transaction transaction, WindowContainer win) {
- transaction.setMatrix(win.getSurfaceControl(), mTransform, mFloat9);
+ applyTransform(transaction, win.getSurfaceControl());
// WindowState sets the position of the window so transform the position and update it.
final float[] winSurfacePos = {win.mLastSurfacePosition.x, win.mLastSurfacePosition.y};
mTransform.mapPoints(winSurfacePos);
@@ -83,6 +82,10 @@ public class SeamlessRotator {
}
}
+ void applyTransform(Transaction t, SurfaceControl sc) {
+ t.setMatrix(sc, mTransform, mFloat9);
+ }
+
/**
* Returns the rotation of the display before it started rotating.
*
@@ -106,14 +109,17 @@ public class SeamlessRotator {
return;
}
- mTransform.reset();
- t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
+ setIdentityMatrix(t, win.mSurfaceControl);
t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
if (mApplyFixedTransformHint) {
t.unsetFixedTransformHint(win.mSurfaceControl);
}
}
+ void setIdentityMatrix(Transaction t, SurfaceControl sc) {
+ t.setMatrix(sc, Matrix.IDENTITY_MATRIX, mFloat9);
+ }
+
public void dump(PrintWriter pw) {
pw.print("{old="); pw.print(mOldRotation); pw.print(", new="); pw.print(mNewRotation);
pw.print("}");
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3e558114abdc..fad87e8875ba 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -319,6 +319,11 @@ class Task extends TaskFragment {
*/
boolean mInResumeTopActivity = false;
+ /**
+ * Used to identify if the activity that is installed from device's system image.
+ */
+ boolean mIsEffectivelySystemApp;
+
int mCurrentUser;
String affinity; // The affinity name for this task, or null; may change identity.
@@ -554,13 +559,24 @@ class Task extends TaskFragment {
if (r.finishing) return false;
- // Set this as the candidate root since it isn't finishing.
- mRoot = r;
+ if (mRoot == null || mRoot.finishing) {
+ // Set this as the candidate root since it isn't finishing.
+ mRoot = r;
+ }
+
+ final int uid = mRoot == r ? effectiveUid : r.info.applicationInfo.uid;
+ if (mIgnoreRelinquishIdentity
+ || (mRoot.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0
+ || (mRoot.info.applicationInfo.uid != Process.SYSTEM_UID
+ && !mRoot.info.applicationInfo.isSystemApp()
+ && mRoot.info.applicationInfo.uid != uid)) {
+ // No need to relinquish identity, end search.
+ return true;
+ }
- // Only end search if we are ignore relinquishing identity or we are not relinquishing.
- return mIgnoreRelinquishIdentity
- || mNeverRelinquishIdentity
- || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
+ // Relinquish to next activity
+ mRoot = r;
+ return false;
}
}
@@ -985,7 +1001,15 @@ class Task extends TaskFragment {
* @param info The activity info which could be different from {@code r.info} if set.
*/
void setIntent(ActivityRecord r, @Nullable Intent intent, @Nullable ActivityInfo info) {
- if (this.intent == null || !mNeverRelinquishIdentity) {
+ boolean updateIdentity = false;
+ if (this.intent == null) {
+ updateIdentity = true;
+ } else if (!mNeverRelinquishIdentity) {
+ final ActivityInfo activityInfo = info != null ? info : r.info;
+ updateIdentity = (effectiveUid == Process.SYSTEM_UID || mIsEffectivelySystemApp
+ || effectiveUid == activityInfo.applicationInfo.uid);
+ }
+ if (updateIdentity) {
mCallingUid = r.launchedFromUid;
mCallingPackage = r.launchedFromPackage;
mCallingFeatureId = r.launchedFromFeatureId;
@@ -998,14 +1022,7 @@ class Task extends TaskFragment {
private void setIntent(Intent _intent, ActivityInfo info) {
if (!isLeafTask()) return;
- if (info.applicationInfo.uid == Process.SYSTEM_UID
- || info.applicationInfo.isSystemApp()) {
- // Only allow the apps that pre-installed on the system image to apply
- // relinquishTaskIdentity
- mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
- } else {
- mNeverRelinquishIdentity = true;
- }
+ mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
affinity = info.taskAffinity;
if (intent == null) {
// If this task already has an intent associated with it, don't set the root
@@ -1014,6 +1031,7 @@ class Task extends TaskFragment {
rootAffinity = affinity;
}
effectiveUid = info.applicationInfo.uid;
+ mIsEffectivelySystemApp = info.applicationInfo.isSystemApp();
stringName = null;
if (info.targetActivity == null) {
@@ -3395,11 +3413,18 @@ class Task extends TaskFragment {
info.topActivityInfo = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.info
: null;
+
+ boolean isTopActivityResumed = mReuseActivitiesReport.top != null
+ && mReuseActivitiesReport.top.getOrganizedTask() == this
+ && mReuseActivitiesReport.top.isState(RESUMED);
// Whether the direct top activity is in size compat mode on foreground.
- info.topActivityInSizeCompat = mReuseActivitiesReport.top != null
- && mReuseActivitiesReport.top.getOrganizedTask() == this
- && mReuseActivitiesReport.top.inSizeCompatMode()
- && mReuseActivitiesReport.top.isState(RESUMED);
+ info.topActivityInSizeCompat = isTopActivityResumed
+ && mReuseActivitiesReport.top.inSizeCompatMode();
+ // Whether the direct top activity requested showing camera compat control.
+ info.cameraCompatControlState = isTopActivityResumed
+ ? mReuseActivitiesReport.top.getCameraCompatControlState()
+ : TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+
info.launchCookies.clear();
info.addLaunchCookie(mLaunchCookie);
forAllActivities(r -> {
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 3d5f9881e044..037d582edc30 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.app.TaskInfo.cameraCompatControlStateToString;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
@@ -931,6 +933,35 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
+ @Override
+ public void updateCameraCompatControlState(WindowContainerToken token, int state) {
+ enforceTaskPermission("updateCameraCompatControlState()");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
+ if (wc == null) {
+ Slog.w(TAG, "Could not resolve window from token");
+ return;
+ }
+ final Task task = wc.asTask();
+ if (task == null) {
+ Slog.w(TAG, "Could not resolve task from token");
+ return;
+ }
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Update camera compat control state to %s for taskId=%d",
+ cameraCompatControlStateToString(state), task.mTaskId);
+ final ActivityRecord activity = task.getTopNonFinishingActivity();
+ if (activity != null) {
+ activity.updateCameraCompatStateFromUser(state);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
public boolean handleInterceptBackPressedOnTaskRoot(Task task) {
if (task == null || !task.isOrganized()
|| !mInterceptBackPressedOnRootTasks.contains(task.mTaskId)) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index dae004dd5f6f..c6948eee5a0c 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -143,6 +143,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
/** The final animation targets derived from participants after promotion. */
private ArraySet<WindowContainer> mTargets = null;
+ /** The main display running this transition. */
+ private DisplayContent mTargetDisplay;
+
/**
* Set of participating windowtokens (activity/wallpaper) which are visible at the end of
* the transition animation.
@@ -365,6 +368,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
t.setCornerRadius(targetLeash, 0);
t.setShadowRadius(targetLeash, 0);
+ t.setMatrix(targetLeash, 1, 0, 0, 1);
// The bounds sent to the transition is always a real bounds. This means we lose
// information about "null" bounds (inheriting from parent). Core will fix-up
// non-organized window surface bounds; however, since Core can't touch organized
@@ -473,6 +477,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
}
+
+ final FadeRotationAnimationController fadeRotationController =
+ mTargetDisplay.getFadeRotationAnimationController();
+ if (fadeRotationController != null) {
+ fadeRotationController.onTransitionFinished();
+ }
}
void abort() {
@@ -514,6 +524,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
if (dc == null) dc = mController.mAtm.mRootWindowContainer.getDefaultDisplay();
+ mTargetDisplay = dc;
if (mState == STATE_ABORT) {
mController.abort(this);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index e24be378d29a..24493e2541e1 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -298,9 +298,9 @@ class WallpaperController {
}
boolean updateWallpaperOffset(WindowState wallpaperWin, boolean sync) {
- final Rect parentFrame = wallpaperWin.getParentFrame();
- final int dw = parentFrame.width();
- final int dh = parentFrame.height();
+ final Rect bounds = wallpaperWin.getLastReportedBounds();
+ final int dw = bounds.width();
+ final int dh = bounds.height();
int xOffset = 0;
int yOffset = 0;
@@ -448,6 +448,13 @@ class WallpaperController {
private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
WindowState target = mWallpaperTarget;
+ if (target == null && changingTarget.mToken.isVisible()
+ && changingTarget.mTransitionController.inTransition()) {
+ // If the wallpaper target was cleared during transition, still allows the visible
+ // window which may have been requested to be invisible to update the offset, e.g.
+ // zoom effect from home.
+ target = changingTarget;
+ }
if (target != null) {
if (target.mWallpaperX >= 0) {
mLastWallpaperX = target.mWallpaperX;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0b9174210a19..8b026bfee15f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2882,6 +2882,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mLastReportedConfiguration.getMergedConfiguration();
}
+ /** Returns the last window configuration bounds reported to the client. */
+ Rect getLastReportedBounds() {
+ final Rect bounds = getLastReportedConfiguration().windowConfiguration.getBounds();
+ return !bounds.isEmpty() ? bounds : getBounds();
+ }
+
void adjustStartingWindowFlags() {
if (mAttrs.type == TYPE_BASE_APPLICATION && mActivityRecord != null
&& mActivityRecord.mStartingWindow != null) {
@@ -5230,6 +5236,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Child window follows parent's scale.
return;
}
+ if (!isVisibleRequested() && !(mIsWallpaper && mToken.isVisible())) {
+ // Skip if it is requested to be invisible, but if it is wallpaper, it may be in
+ // transition that still needs to update the scale for zoom effect.
+ return;
+ }
float newHScale = mHScale * mGlobalScale * mWallpaperScale;
float newVScale = mVScale * mGlobalScale * mWallpaperScale;
if (mLastHScale != newHScale ||
@@ -5249,7 +5260,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
updateSurfacePositionNonOrganized();
// Send information to SurfaceFlinger about the priority of the current window.
updateFrameRateSelectionPriorityIfNeeded();
- if (isVisibleRequested()) updateScaleIfNeeded();
+ updateScaleIfNeeded();
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
super.prepareSurfaces();
@@ -5275,11 +5286,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mSurfacePosition);
if (mWallpaperScale != 1f) {
- DisplayInfo displayInfo = getDisplayInfo();
+ final Rect bounds = getLastReportedBounds();
Matrix matrix = mTmpMatrix;
matrix.setTranslate(mXOffset, mYOffset);
- matrix.postScale(mWallpaperScale, mWallpaperScale, displayInfo.logicalWidth / 2f,
- displayInfo.logicalHeight / 2f);
+ matrix.postScale(mWallpaperScale, mWallpaperScale, bounds.exactCenterX(),
+ bounds.exactCenterY());
matrix.getValues(mTmpMatrixArray);
mSurfacePosition.offset(Math.round(mTmpMatrixArray[Matrix.MTRANS_X]),
Math.round(mTmpMatrixArray[Matrix.MTRANS_Y]));
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index c5a69e2f84b7..6aa632388ffd 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -2292,6 +2292,11 @@ static jboolean nativeFlushSensor(JNIEnv* env, jclass /* clazz */, jlong ptr, ji
sensorType));
}
+static void nativeCancelCurrentTouch(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ im->getInputManager()->getDispatcher().cancelCurrentTouch();
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gInputManagerMethods[] = {
@@ -2371,6 +2376,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"nativeEnableSensor", "(JIIII)Z", (void*)nativeEnableSensor},
{"nativeDisableSensor", "(JII)V", (void*)nativeDisableSensor},
{"nativeFlushSensor", "(JII)Z", (void*)nativeFlushSensor},
+ {"nativeCancelCurrentTouch", "(J)V", (void*)nativeCancelCurrentTouch},
};
#define FIND_CLASS(var, className) \
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 429edf175be4..2f4dd57ab15b 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -26,6 +26,10 @@
<xs:element name="displayConfiguration">
<xs:complexType>
<xs:sequence>
+ <xs:element type="densityMap" name="densityMap" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
<xs:element type="nitsMap" name="screenBrightnessMap">
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
@@ -181,5 +185,27 @@
</xs:sequence>
</xs:complexType>
+ <xs:complexType name="densityMap">
+ <xs:sequence>
+ <xs:element name="density" type="density" maxOccurs="unbounded" minOccurs="1">
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="density">
+ <xs:sequence>
+ <xs:element type="xs:nonNegativeInteger" name="width">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element type="xs:nonNegativeInteger" name="height">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element type="xs:nonNegativeInteger" name="density">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
</xs:schema>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index ad186026d30c..5b2b87c3f14e 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -1,8 +1,24 @@
// Signature format: 2.0
package com.android.server.display.config {
+ public class Density {
+ ctor public Density();
+ method @NonNull public final java.math.BigInteger getDensity();
+ method @NonNull public final java.math.BigInteger getHeight();
+ method @NonNull public final java.math.BigInteger getWidth();
+ method public final void setDensity(@NonNull java.math.BigInteger);
+ method public final void setHeight(@NonNull java.math.BigInteger);
+ method public final void setWidth(@NonNull java.math.BigInteger);
+ }
+
+ public class DensityMap {
+ ctor public DensityMap();
+ method public java.util.List<com.android.server.display.config.Density> getDensity();
+ }
+
public class DisplayConfiguration {
ctor public DisplayConfiguration();
+ method @Nullable public final com.android.server.display.config.DensityMap getDensityMap();
method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
method public final com.android.server.display.config.SensorDetails getLightSensor();
method public final com.android.server.display.config.SensorDetails getProxSensor();
@@ -13,6 +29,7 @@ package com.android.server.display.config {
method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
+ method public final void setDensityMap(@Nullable com.android.server.display.config.DensityMap);
method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
method public final void setLightSensor(com.android.server.display.config.SensorDetails);
method public final void setProxSensor(com.android.server.display.config.SensorDetails);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bfceeda08e3d..266b65686141 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -91,18 +91,18 @@ import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_PRE_CONDITION_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_PROFILE_CREATION_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_STARTING_PROFILE_FAILED;
import static android.app.admin.DevicePolicyManager.STATE_USER_UNMANAGED;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY;
+import static android.app.admin.ProvisioningException.ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_PRE_CONDITION_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_PROFILE_CREATION_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_REMOVE_NON_REQUIRED_APPS_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_SETTING_PROFILE_OWNER_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
@@ -3242,9 +3242,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private void performPolicyVersionUpgrade() {
- PolicyVersionUpgrader upgrader = new PolicyVersionUpgrader(
- new DpmsUpgradeDataProvider());
-
+ PolicyVersionUpgrader upgrader = new PolicyVersionUpgrader(new DpmsUpgradeDataProvider());
upgrader.upgradePolicy(DPMS_VERSION);
}
@@ -3998,6 +3996,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ // System caller can query policy for a particular admin.
+ Preconditions.checkCallAuthorization(
+ who == null || isCallingFromPackage(who.getPackageName(), caller.getUid())
+ || canQueryAdminPolicy(caller));
synchronized (getLockObject()) {
int mode = PASSWORD_QUALITY_UNSPECIFIED;
@@ -4213,7 +4215,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
synchronized (getLockObject()) {
@@ -4363,7 +4365,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
synchronized (getLockObject()) {
@@ -4576,7 +4578,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
synchronized (getLockObject()) {
@@ -4996,6 +4998,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ // System caller can query policy for a particular admin.
+ Preconditions.checkCallAuthorization(
+ who == null || isCallingFromPackage(who.getPackageName(), caller.getUid())
+ || canQueryAdminPolicy(caller));
synchronized (getLockObject()) {
ActiveAdmin admin = (who != null)
@@ -5307,6 +5313,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ // System caller can query policy for a particular admin.
+ Preconditions.checkCallAuthorization(
+ who == null || isCallingFromPackage(who.getPackageName(), caller.getUid())
+ || canQueryAdminPolicy(caller));
synchronized (getLockObject()) {
if (who != null) {
@@ -5384,7 +5394,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
- final CallerIdentity caller = getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
if (!mLockPatternUtils.hasSecureLockScreen()) {
@@ -7727,6 +7737,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return false;
}
+
+ final CallerIdentity caller = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+
if (parent) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId()));
@@ -9509,8 +9523,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private boolean canManageUsers(CallerIdentity caller) {
- return isSystemUid(caller) || isRootUid(caller)
- || hasCallingOrSelfPermission(permission.MANAGE_USERS);
+ return hasCallingOrSelfPermission(permission.MANAGE_USERS);
+ }
+
+ private boolean canQueryAdminPolicy(CallerIdentity caller) {
+ return hasCallingOrSelfPermission(permission.QUERY_ADMIN_POLICY);
}
private boolean hasPermission(String permission, int pid, int uid) {
@@ -9958,7 +9975,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(agent, "agent null");
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentity(admin);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
synchronized (getLockObject()) {
@@ -10238,8 +10255,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return null;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
- || hasCallingOrSelfPermission(permission.QUERY_ADMIN_POLICY));
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(canManageUsers(caller) || canQueryAdminPolicy(caller));
synchronized (getLockObject()) {
List<String> result = null;
@@ -10410,8 +10427,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public @Nullable List<String> getPermittedInputMethodsAsUser(@UserIdInt int userId) {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
- Preconditions.checkCallAuthorization(canManageUsers(caller)
- || hasCallingOrSelfPermission(permission.QUERY_ADMIN_POLICY));
+ Preconditions.checkCallAuthorization(canManageUsers(caller) || canQueryAdminPolicy(caller));
final long callingIdentity = Binder.clearCallingIdentity();
try {
return getPermittedInputMethodsUnchecked(userId);
@@ -10653,15 +10669,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
final int userHandle = user.getIdentifier();
- final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_USER_CREATED)
- .putExtra(Intent.EXTRA_USER_HANDLE, userHandle)
- .putExtra(
- DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
- leaveAllSystemAppsEnabled)
- .setPackage(getManagedProvisioningPackage(mContext))
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-
final long id = mInjector.binderClearCallingIdentity();
try {
manageUserUnchecked(admin, profileOwner, userHandle, adminExtras,
@@ -10672,6 +10679,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Settings.Secure.USER_SETUP_COMPLETE, 1, userHandle);
}
+ sendProvisioningCompletedBroadcast(
+ userHandle, ACTION_PROVISION_MANAGED_USER, leaveAllSystemAppsEnabled);
+
return user;
} catch (Throwable re) {
mUserManager.removeUser(userHandle);
@@ -10686,6 +10696,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ private void sendProvisioningCompletedBroadcast(
+ int user, String action, boolean leaveAllSystemAppsEnabled) {
+ final Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISIONING_COMPLETED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, user)
+ .putExtra(
+ DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
+ leaveAllSystemAppsEnabled)
+ .putExtra(DevicePolicyManager.EXTRA_PROVISIONING_ACTION,
+ action)
+ .setPackage(getManagedProvisioningPackage(mContext))
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ }
+
private void manageUserUnchecked(ComponentName admin, ComponentName profileOwner,
@UserIdInt int userId, @Nullable PersistableBundle adminExtras,
boolean showDisclaimer) {
@@ -17230,7 +17254,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ACTION_PROVISION_MANAGED_PROFILE, admin.getPackageName());
if (result != CODE_OK) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_PRE_CONDITION_FAILED,
+ ERROR_PRE_CONDITION_FAILED,
"Provisioning preconditions failed with result: " + result);
}
@@ -17247,7 +17271,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
nonRequiredApps.toArray(new String[nonRequiredApps.size()]));
if (userInfo == null) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_PROFILE_CREATION_FAILED,
+ ERROR_PROFILE_CREATION_FAILED,
"Error creating profile, createProfileForUserEvenWhenDisallowed "
+ "returned null.");
}
@@ -17261,7 +17285,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!enableAdminAndSetProfileOwner(
userInfo.id, caller.getUserId(), admin, provisioningParams.getOwnerName())) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED,
+ ERROR_SETTING_PROFILE_OWNER_FAILED,
"Error setting profile owner.");
}
setUserSetupComplete(userInfo.id);
@@ -17269,7 +17293,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
startUser(userInfo.id, callerPackage);
maybeMigrateAccount(
userInfo.id, caller.getUserId(), provisioningParams.getAccountToMigrate(),
- provisioningParams.isKeepAccountMigrated(), callerPackage);
+ provisioningParams.isKeepingAccountOnMigration(), callerPackage);
if (provisioningParams.isOrganizationOwnedProvisioning()) {
synchronized (getLockObject()) {
@@ -17277,6 +17301,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ sendProvisioningCompletedBroadcast(
+ userInfo.id,
+ ACTION_PROVISION_MANAGED_PROFILE,
+ provisioningParams.isLeaveAllSystemAppsEnabled());
+
return userInfo.getUserHandle();
} catch (Exception e) {
DevicePolicyEventLogger
@@ -17336,14 +17365,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
userId);
if (status != PackageManager.INSTALL_SUCCEEDED) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED,
+ ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED,
String.format("Failed to install existing package %s for user %d with "
+ "result code %d",
packageName, userId, status));
}
} catch (NameNotFoundException e) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED,
+ ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED,
String.format("Failed to install existing package %s for user %d: %s",
packageName, userId, e.getMessage()));
}
@@ -17401,12 +17430,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/* scheduler= */ null);
try {
if (!mInjector.getIActivityManager().startUserInBackground(userId)) {
- throw new ServiceSpecificException(PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
+ throw new ServiceSpecificException(ERROR_STARTING_PROFILE_FAILED,
String.format("Unable to start user %d in background", userId));
}
if (!unlockedReceiver.waitForUserUnlocked()) {
- throw new ServiceSpecificException(PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
+ throw new ServiceSpecificException(ERROR_STARTING_PROFILE_FAILED,
String.format("Timeout whilst waiting for unlock of user %d.", userId));
}
logEventDuration(
@@ -17529,7 +17558,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
if (result != CODE_OK) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_PRE_CONDITION_FAILED,
+ ERROR_PRE_CONDITION_FAILED,
"Provisioning preconditions failed with result: " + result);
}
setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
@@ -17542,7 +17571,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
provisioningParams.isLeaveAllSystemAppsEnabled(),
deviceAdmin)) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED,
+ ERROR_REMOVE_NON_REQUIRED_APPS_FAILED,
"PackageManager failed to remove non required apps.");
}
@@ -17550,13 +17579,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!setActiveAdminAndDeviceOwner(
deviceOwnerUserId, deviceAdmin, provisioningParams.getOwnerName())) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED,
+ ERROR_SET_DEVICE_OWNER_FAILED,
"Failed to set device owner.");
}
disallowAddUser();
setAdminCanGrantSensorsPermissionForUserUnchecked(deviceOwnerUserId,
provisioningParams.canDeviceOwnerGrantSensorsPermissions());
+ sendProvisioningCompletedBroadcast(
+ deviceOwnerUserId,
+ ACTION_PROVISION_MANAGED_DEVICE,
+ provisioningParams.isLeaveAllSystemAppsEnabled());
} catch (Exception e) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a72cf3a79d37..663e17bc9784 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -151,6 +151,7 @@ import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.NativeTombstoneManagerService;
import com.android.server.os.SchedulingPolicyService;
import com.android.server.people.PeopleService;
+import com.android.server.pm.ApexManager;
import com.android.server.pm.CrossProfileAppsService;
import com.android.server.pm.DataLoaderManagerService;
import com.android.server.pm.DynamicCodeLoggingService;
@@ -220,6 +221,7 @@ import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.Locale;
+import java.util.Map;
import java.util.Timer;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
@@ -332,6 +334,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.contentcapture.ContentCaptureManagerService";
private static final String TRANSLATION_MANAGER_SERVICE_CLASS =
"com.android.server.translation.TranslationManagerService";
+ private static final String SELECTION_TOOLBAR_MANAGER_SERVICE_CLASS =
+ "com.android.server.selectiontoolbar.SelectionToolbarManagerService";
private static final String MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS =
"com.android.server.musicrecognition.MusicRecognitionManagerService";
private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS =
@@ -916,6 +920,13 @@ public final class SystemServer implements Dumpable {
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
+ // Apex services must be the last category of services to start. No other service must
+ // be starting after this point. This is to prevent unnessary stability issues when
+ // these apexes are updated outside of OTA; and to avoid breaking dependencies from
+ // system into apexes.
+ // TODO(satayev): lock mSystemServiceManager.startService to stop accepting new services
+ // after this step
+ startApexServices(t);
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
@@ -2592,6 +2603,11 @@ public final class SystemServer implements Dumpable {
Slog.d(TAG, "TranslationService not defined by OEM");
}
+ // Selection toolbar service
+ t.traceBegin("StartSelectionToolbarManagerService");
+ mSystemServiceManager.startService(SELECTION_TOOLBAR_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
+
// NOTE: ClipboardService depends on ContentCapture and Autofill
t.traceBegin("StartClipboardService");
mSystemServiceManager.startService(ClipboardService.class);
@@ -3044,6 +3060,27 @@ public final class SystemServer implements Dumpable {
t.traceEnd(); // startOtherServices
}
+ /**
+ * Starts system services defined in apexes.
+ */
+ private void startApexServices(@NonNull TimingsTraceAndSlog t) {
+ t.traceBegin("startApexServices");
+ Map<String, String> services = ApexManager.getInstance().getApexSystemServices();
+ // TODO(satayev): filter out already started services
+ // TODO(satayev): introduce android:order for services coming the same apexes
+ for (String name : new TreeSet<>(services.keySet())) {
+ String jarPath = services.get(name);
+ t.traceBegin("starting " + name);
+ if (TextUtils.isEmpty(jarPath)) {
+ mSystemServiceManager.startService(name);
+ } else {
+ mSystemServiceManager.startServiceFromJar(name, jarPath);
+ }
+ t.traceEnd();
+ }
+ t.traceEnd(); // startApexServices
+ }
+
private boolean deviceHasConfigString(@NonNull Context context, @StringRes int resId) {
String serviceName = context.getString(resId);
return !TextUtils.isEmpty(serviceName);
diff --git a/services/selectiontoolbar/Android.bp b/services/selectiontoolbar/Android.bp
new file mode 100644
index 000000000000..cc6405f97bc3
--- /dev/null
+++ b/services/selectiontoolbar/Android.bp
@@ -0,0 +1,22 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "services.selectiontoolbar-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.selectiontoolbar",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.selectiontoolbar-sources"],
+ libs: ["services.core"],
+}
diff --git a/services/selectiontoolbar/OWNERS b/services/selectiontoolbar/OWNERS
new file mode 100644
index 000000000000..ed9425cc26c9
--- /dev/null
+++ b/services/selectiontoolbar/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/selectiontoolbar/OWNERS
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java
new file mode 100644
index 000000000000..c26965dadff1
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+import android.service.selectiontoolbar.SelectionToolbarRenderService;
+import android.util.Log;
+import android.view.selectiontoolbar.ShowInfo;
+
+/**
+ * The default implementation of {@link SelectionToolbarRenderService}.
+ */
+public final class DefaultSelectionToolbarRenderService extends SelectionToolbarRenderService {
+
+ private static final String TAG = "DefaultSelectionToolbarRenderService";
+
+ @Override
+ public void onShow(ShowInfo showInfo,
+ SelectionToolbarRenderService.RemoteCallbackWrapper callbackWrapper) {
+ // TODO: Add implementation
+ Log.w(TAG, "onShow()");
+ }
+
+ @Override
+ public void onHide(long widgetToken) {
+ // TODO: Add implementation
+ Log.w(TAG, "onHide()");
+ }
+
+ @Override
+ public void onDismiss(long widgetToken) {
+ // TODO: Add implementation
+ Log.w(TAG, "onDismiss()");
+ }
+}
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java
new file mode 100644
index 000000000000..ced24e00da63
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.service.selectiontoolbar.ISelectionToolbarRenderService;
+import android.service.selectiontoolbar.SelectionToolbarRenderService;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+
+import com.android.internal.infra.AbstractRemoteService;
+import com.android.internal.infra.ServiceConnector;
+
+final class RemoteSelectionToolbarRenderService extends
+ ServiceConnector.Impl<ISelectionToolbarRenderService> {
+ private static final String TAG = "RemoteSelectionToolbarRenderService";
+
+ private static final long TIMEOUT_IDLE_UNBIND_MS =
+ AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
+
+ private final ComponentName mComponentName;
+
+
+ RemoteSelectionToolbarRenderService(Context context, ComponentName serviceName, int userId) {
+ super(context, new Intent(SelectionToolbarRenderService.SERVICE_INTERFACE).setComponent(
+ serviceName), 0, userId, ISelectionToolbarRenderService.Stub::asInterface);
+ mComponentName = serviceName;
+ // Bind right away.
+ connect();
+ }
+
+ @Override // from AbstractRemoteService
+ protected long getAutoDisconnectTimeoutMs() {
+ return TIMEOUT_IDLE_UNBIND_MS;
+ }
+
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ public void onShow(ShowInfo showInfo, ISelectionToolbarCallback callback) {
+ run((s) -> s.onShow(showInfo, callback));
+ }
+
+ public void onHide(long widgetToken) {
+ run((s) -> s.onHide(widgetToken));
+ }
+
+ public void onDismiss(long widgetToken) {
+ run((s) -> s.onDismiss(widgetToken));
+ }
+}
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerService.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerService.java
new file mode 100644
index 000000000000..3bdf55ccb5c8
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+import android.content.Context;
+import android.util.Slog;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ISelectionToolbarManager;
+import android.view.selectiontoolbar.ShowInfo;
+
+import com.android.internal.util.DumpUtils;
+import com.android.server.infra.AbstractMasterSystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Entry point service for selection toolbar management.
+ */
+public final class SelectionToolbarManagerService extends
+ AbstractMasterSystemService<SelectionToolbarManagerService,
+ SelectionToolbarManagerServiceImpl> {
+
+ private static final String TAG = "SelectionToolbarManagerService";
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.SELECTION_TOOLBAR_SERVICE,
+ new SelectionToolbarManagerService.SelectionToolbarManagerServiceStub());
+ }
+
+ public SelectionToolbarManagerService(Context context) {
+ super(context, new SelectionToolbarServiceNameResolver(), /* disallowProperty= */
+ null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
+ }
+
+ @Override
+ protected SelectionToolbarManagerServiceImpl newServiceLocked(int resolvedUserId,
+ boolean disabled) {
+ return new SelectionToolbarManagerServiceImpl(this, mLock, resolvedUserId);
+ }
+
+ final class SelectionToolbarManagerServiceStub extends ISelectionToolbarManager.Stub {
+
+ @Override
+ public void showToolbar(ShowInfo showInfo, ISelectionToolbarCallback callback, int userId) {
+ synchronized (mLock) {
+ SelectionToolbarManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.showToolbar(showInfo, callback);
+ } else {
+ Slog.v(TAG, "showToolbar(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void hideToolbar(long widgetToken, int userId) {
+ synchronized (mLock) {
+ SelectionToolbarManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.hideToolbar(widgetToken);
+ } else {
+ Slog.v(TAG, "hideToolbar(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void dismissToolbar(long widgetToken, int userId) {
+ synchronized (mLock) {
+ SelectionToolbarManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.dismissToolbar(widgetToken);
+ } else {
+ Slog.v(TAG, "dismissToolbar(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
+
+ synchronized (mLock) {
+ dumpLocked("", pw);
+ }
+ }
+ }
+}
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
new file mode 100644
index 000000000000..235f547feed0
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+final class SelectionToolbarManagerServiceImpl extends
+ AbstractPerUserSystemService<SelectionToolbarManagerServiceImpl,
+ SelectionToolbarManagerService> {
+
+ private static final String TAG = "SelectionToolbarManagerServiceImpl";
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteSelectionToolbarRenderService mRemoteService;
+
+ protected SelectionToolbarManagerServiceImpl(@NonNull SelectionToolbarManagerService master,
+ @NonNull Object lock, int userId) {
+ super(master, lock, userId);
+ updateRemoteServiceLocked();
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+ throws PackageManager.NameNotFoundException {
+ return getServiceInfoOrThrow(serviceComponent, mUserId);
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected boolean updateLocked(boolean disabled) {
+ final boolean enabledChanged = super.updateLocked(disabled);
+ updateRemoteServiceLocked();
+ return enabledChanged;
+ }
+
+ /**
+ * Updates the reference to the remote service.
+ */
+ @GuardedBy("mLock")
+ private void updateRemoteServiceLocked() {
+ if (mRemoteService != null) {
+ Slog.d(TAG, "updateRemoteService(): destroying old remote service");
+ mRemoteService.unbind();
+ mRemoteService = null;
+ }
+ }
+
+ @GuardedBy("mLock")
+ void showToolbar(ShowInfo showInfo, ISelectionToolbarCallback callback) {
+ final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
+ if (remoteService != null) {
+ remoteService.onShow(showInfo, callback);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void hideToolbar(long widgetToken) {
+ final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
+ if (remoteService != null) {
+ remoteService.onHide(widgetToken);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void dismissToolbar(long widgetToken) {
+ final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
+ if (remoteService != null) {
+ remoteService.onDismiss(widgetToken);
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteSelectionToolbarRenderService ensureRemoteServiceLocked() {
+ if (mRemoteService == null) {
+ final String serviceName = getComponentNameLocked();
+ final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+ mRemoteService = new RemoteSelectionToolbarRenderService(getContext(), serviceComponent,
+ mUserId);
+ }
+ return mRemoteService;
+ }
+
+ private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, @UserIdInt int userId)
+ throws PackageManager.NameNotFoundException {
+ int flags = PackageManager.GET_META_DATA;
+
+ ServiceInfo si = null;
+ try {
+ si = AppGlobals.getPackageManager().getServiceInfo(comp, flags, userId);
+ } catch (RemoteException e) {
+ }
+ if (si == null) {
+ throw new PackageManager.NameNotFoundException("Could not get serviceInfo for "
+ + comp.flattenToShortString());
+ }
+ return si;
+ }
+}
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java
new file mode 100644
index 000000000000..1d4c94dadb75
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.selectiontoolbar;
+
+import com.android.server.infra.ServiceNameResolver;
+
+import java.io.PrintWriter;
+
+final class SelectionToolbarServiceNameResolver implements ServiceNameResolver {
+
+ // TODO: move to SysUi or ExtServices
+ private static final String SELECTION_TOOLBAR_SERVICE_NAME =
+ "android/com.android.server.selectiontoolbar.DefaultSelectionToolbarRenderService";
+
+ @Override
+ public String getDefaultServiceName(int userId) {
+ return SELECTION_TOOLBAR_SERVICE_NAME;
+ }
+
+ @Override
+ public void dumpShort(PrintWriter pw) {
+ pw.print("service="); pw.print(SELECTION_TOOLBAR_SERVICE_NAME);
+ }
+
+ @Override
+ public void dumpShort(PrintWriter pw, int userId) {
+ pw.print("defaultService="); pw.print(getDefaultServiceName(userId));
+ }
+}
diff --git a/services/tests/mockingservicestests/assets/GameServiceSelectorTest/game_service_metadata_valid.xml b/services/tests/mockingservicestests/assets/GameServiceSelectorTest/game_service_metadata_valid.xml
new file mode 100644
index 000000000000..4720085e03fc
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/GameServiceSelectorTest/game_service_metadata_valid.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<game-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:sessionService="com.game.service.provider.GameSessionService"/>
diff --git a/services/tests/mockingservicestests/res/xml/game_service_metadata_no_session_service.xml b/services/tests/mockingservicestests/res/xml/game_service_metadata_no_session_service.xml
new file mode 100644
index 000000000000..ebd5103a24ff
--- /dev/null
+++ b/services/tests/mockingservicestests/res/xml/game_service_metadata_no_session_service.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<game-service/>
diff --git a/services/tests/mockingservicestests/res/xml/game_service_metadata_valid.xml b/services/tests/mockingservicestests/res/xml/game_service_metadata_valid.xml
new file mode 100644
index 000000000000..8ee3cceee8ea
--- /dev/null
+++ b/services/tests/mockingservicestests/res/xml/game_service_metadata_valid.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<game-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:gameSessionService="com.game.service.provider.GameSessionService"/>
diff --git a/services/tests/mockingservicestests/res/xml/game_service_metadata_wrong_first_tag.xml b/services/tests/mockingservicestests/res/xml/game_service_metadata_wrong_first_tag.xml
new file mode 100644
index 000000000000..6bc0eac551bc
--- /dev/null
+++ b/services/tests/mockingservicestests/res/xml/game_service_metadata_wrong_first_tag.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<wrong-tag xmlns:android="http://schemas.android.com/apk/res/android"
+ android:gameSessionService="com.game.service.provider.GameSessionService"/>
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/FakeGameClassifier.java b/services/tests/mockingservicestests/src/com/android/server/app/FakeGameClassifier.java
new file mode 100644
index 000000000000..060b773d7b7b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/app/FakeGameClassifier.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import android.annotation.NonNull;
+import android.os.UserHandle;
+
+import java.util.HashSet;
+
+/**
+ * Fake implementation of {@link GameClassifier} used for tests.
+ *
+ * By default, all packages are considers not games. A package may be marked as a game using
+ * {@link #recordGamePackage(String)}.
+ */
+final class FakeGameClassifier implements GameClassifier {
+ private final HashSet<String> mGamePackages = new HashSet<>();
+
+ /**
+ * Marks the given {@code packageName} as a game.
+ */
+ public void recordGamePackage(String packageName) {
+ mGamePackages.add(packageName);
+ }
+
+ @Override
+ public boolean isGame(@NonNull String packageName, UserHandle userHandle) {
+ return mGamePackages.contains(packageName);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/FakeGameServiceProviderInstance.java b/services/tests/mockingservicestests/src/com/android/server/app/FakeGameServiceProviderInstance.java
new file mode 100644
index 000000000000..98142f5774a0
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/app/FakeGameServiceProviderInstance.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+
+/**
+ * Fake implementation of {@link GameServiceProviderInstance} used for tests.
+ */
+final class FakeGameServiceProviderInstance implements GameServiceProviderInstance {
+ private boolean mRunning;
+
+ @Override
+ public void start() {
+ mRunning = true;
+ }
+
+ @Override
+ public void stop() {
+ mRunning = false;
+ }
+
+ /**
+ * Returns {@code true} if the instance is currently running.
+ */
+ public boolean getIsRunning() {
+ return mRunning;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/FakeServiceConnector.java b/services/tests/mockingservicestests/src/com/android/server/app/FakeServiceConnector.java
new file mode 100644
index 000000000000..0ae509ec0fbc
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/app/FakeServiceConnector.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+
+import android.os.IInterface;
+
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.infra.ServiceConnector;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Fake implementation of {@link ServiceConnector<T>} used for tests.
+ *
+ * Tests provide a service instance via {@link #FakeServiceConnector(IInterface)} that will be
+ * connected to and used to fulfill service jobs.
+ */
+final class FakeServiceConnector<T extends IInterface> implements
+ ServiceConnector<T> {
+ private final T mService;
+ private boolean mIsConnected;
+ private int mConnectCount = 0;
+
+ FakeServiceConnector(T service) {
+ mService = service;
+ }
+
+ @Override
+ public boolean run(VoidJob<T> job) {
+ AndroidFuture<Void> unusedFuture = post(job);
+ return true;
+ }
+
+ @Override
+ public AndroidFuture<Void> post(VoidJob<T> job) {
+ markPossibleConnection();
+
+ return postForResult(job);
+ }
+
+ @Override
+ public <R> AndroidFuture<R> postForResult(Job<T, R> job) {
+ markPossibleConnection();
+
+ AndroidFuture<R> androidFuture = new AndroidFuture();
+ try {
+ androidFuture.complete(job.run(mService));
+ } catch (Exception ex) {
+ androidFuture.completeExceptionally(ex);
+ }
+ return androidFuture;
+ }
+
+ @Override
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public <R> AndroidFuture<R> postAsync(Job<T, CompletableFuture<R>> job) {
+ markPossibleConnection();
+ AndroidFuture<R> androidFuture = new AndroidFuture();
+
+ try {
+ CompletableFuture<R> future = job.run(mService);
+ future.whenComplete((result, exception) -> {
+ if (exception != null) {
+ androidFuture.completeExceptionally(exception);
+ } else {
+ androidFuture.complete(result);
+ }
+ });
+ } catch (Exception ex) {
+ androidFuture.completeExceptionally(ex);
+ }
+
+ return androidFuture;
+ }
+
+ @Override
+ public AndroidFuture<T> connect() {
+ markPossibleConnection();
+ return AndroidFuture.completedFuture(mService);
+ }
+
+ @Override
+ public void unbind() {
+ mIsConnected = false;
+ }
+
+ private void markPossibleConnection() {
+ if (mIsConnected) {
+ return;
+ }
+
+ mConnectCount += 1;
+ mIsConnected = true;
+ }
+
+ /**
+ * Returns {@code true} if the underlying service is connected.
+ */
+ public boolean getIsConnected() {
+ return mIsConnected;
+ }
+
+ /**
+ * Returns the number of times a connection was established with the underlying service.
+ */
+ public int getConnectCount() {
+ return mConnectCount;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java
new file mode 100644
index 000000000000..0545fde3e921
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.ConcurrentUtils;
+import com.android.server.SystemService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+
+/** Unit tests for {@link GameServiceController}. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public final class GameServiceControllerTest {
+ private static final UserHandle USER_HANDLE_10 = new UserHandle(10);
+ private static final UserHandle USER_HANDLE_11 = new UserHandle(11);
+ private static final SystemService.TargetUser USER_10 = user(10);
+ private static final SystemService.TargetUser USER_11 = user(11);
+ private static final String PROVIDER_A_PACKAGE_NAME = "com.provider.a";
+ private static final ComponentName PROVIDER_A_SERVICE_A =
+ new ComponentName(PROVIDER_A_PACKAGE_NAME, "com.provider.a.ServiceA");
+ private static final ComponentName PROVIDER_A_SERVICE_B =
+ new ComponentName(PROVIDER_A_PACKAGE_NAME, "com.provider.a.ServiceB");
+
+ private MockitoSession mMockingSession;
+ private GameServiceController mGameServiceManager;
+ @Mock
+ private GameServiceProviderSelector mMockGameServiceProviderSelector;
+ @Mock
+ private GameServiceProviderInstanceFactory mMockGameServiceProviderInstanceFactory;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ mGameServiceManager = new GameServiceController(
+ ConcurrentUtils.DIRECT_EXECUTOR,
+ mMockGameServiceProviderSelector,
+ mMockGameServiceProviderInstanceFactory);
+ }
+
+ @After
+ public void tearDown() {
+ mMockingSession.finishMocking();
+ }
+
+ @Test
+ public void notifyUserStarted_hasNotCompletedBoot_doesNothing() {
+ mGameServiceManager.notifyUserStarted(USER_10);
+
+ verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory);
+ }
+
+ @Test
+ public void notifyUserStarted_createsAndStartsNewInstance() {
+ GameServiceProviderConfiguration configurationA =
+ new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A,
+ PROVIDER_A_SERVICE_B);
+ FakeGameServiceProviderInstance instanceA =
+ seedConfigurationForUser(USER_10, configurationA);
+
+ mGameServiceManager.onBootComplete();
+ mGameServiceManager.notifyUserStarted(USER_10);
+
+ verify(mMockGameServiceProviderInstanceFactory).create(configurationA);
+ verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory);
+ assertThat(instanceA.getIsRunning()).isTrue();
+ }
+
+ @Test
+ public void notifyUserStarted_sameUser_doesNotCreateNewInstance() {
+ GameServiceProviderConfiguration configurationA =
+ new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A,
+ PROVIDER_A_SERVICE_B);
+ FakeGameServiceProviderInstance instanceA =
+ seedConfigurationForUser(USER_10, configurationA);
+
+ mGameServiceManager.onBootComplete();
+ mGameServiceManager.notifyUserStarted(USER_10);
+ mGameServiceManager.notifyUserStarted(USER_10);
+
+ verify(mMockGameServiceProviderInstanceFactory).create(configurationA);
+ verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory);
+ assertThat(instanceA.getIsRunning()).isTrue();
+ }
+
+ @Test
+ public void notifyUserUnlocking_noForegroundUser_ignores() {
+ GameServiceProviderConfiguration configurationA =
+ new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A,
+ PROVIDER_A_SERVICE_B);
+ FakeGameServiceProviderInstance instanceA =
+ seedConfigurationForUser(USER_10, configurationA);
+
+ mGameServiceManager.onBootComplete();
+ mGameServiceManager.notifyUserUnlocking(USER_10);
+
+ verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory);
+ assertThat(instanceA.getIsRunning()).isFalse();
+ }
+
+ @Test
+ public void notifyUserUnlocking_sameAsForegroundUser_evaluatesProvider() {
+ GameServiceProviderConfiguration configurationA =
+ new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A,
+ PROVIDER_A_SERVICE_B);
+ seedNoConfigurationForUser(USER_10);
+
+ mGameServiceManager.onBootComplete();
+ mGameServiceManager.notifyUserStarted(USER_10);
+ FakeGameServiceProviderInstance instanceA =
+ seedConfigurationForUser(USER_10, configurationA);
+ mGameServiceManager.notifyUserUnlocking(USER_10);
+
+ verify(mMockGameServiceProviderInstanceFactory).create(configurationA);
+ verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory);
+ assertThat(instanceA.getIsRunning()).isTrue();
+ }
+
+ @Test
+ public void notifyUserUnlocking_differentFromForegroundUser_ignores() {
+ GameServiceProviderConfiguration configurationA =
+ new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A,
+ PROVIDER_A_SERVICE_B);
+ seedNoConfigurationForUser(USER_10);
+
+ mGameServiceManager.onBootComplete();
+ mGameServiceManager.notifyUserStarted(USER_10);
+ FakeGameServiceProviderInstance instanceA =
+ seedConfigurationForUser(USER_11, configurationA);
+ mGameServiceManager.notifyUserUnlocking(USER_11);
+
+ verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory);
+ assertThat(instanceA.getIsRunning()).isFalse();
+ }
+
+ @Test
+ public void
+ notifyNewForegroundUser_differentUser_stopsPreviousInstanceAndThenStartsNewInstance() {
+ GameServiceProviderConfiguration configurationA =
+ new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A,
+ PROVIDER_A_SERVICE_B);
+ FakeGameServiceProviderInstance instanceA =
+ seedConfigurationForUser(USER_10, configurationA);
+ GameServiceProviderConfiguration configurationB =
+ new GameServiceProviderConfiguration(USER_HANDLE_11, PROVIDER_A_SERVICE_A,
+ PROVIDER_A_SERVICE_B);
+ FakeGameServiceProviderInstance instanceB = seedConfigurationForUser(USER_11,
+ configurationB);
+ InOrder instancesInOrder = Mockito.inOrder(instanceA, instanceB);
+
+ mGameServiceManager.onBootComplete();
+ mGameServiceManager.notifyUserStarted(USER_10);
+ mGameServiceManager.notifyNewForegroundUser(USER_11);
+
+ verify(mMockGameServiceProviderInstanceFactory).create(configurationA);
+ verify(mMockGameServiceProviderInstanceFactory).create(configurationB);
+ instancesInOrder.verify(instanceA).start();
+ instancesInOrder.verify(instanceA).stop();
+ instancesInOrder.verify(instanceB).start();
+ verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory);
+ assertThat(instanceA.getIsRunning()).isFalse();
+ assertThat(instanceB.getIsRunning()).isTrue();
+ }
+
+ private void seedNoConfigurationForUser(SystemService.TargetUser user) {
+ when(mMockGameServiceProviderSelector.get(user)).thenReturn(null);
+ }
+
+ private FakeGameServiceProviderInstance seedConfigurationForUser(SystemService.TargetUser user,
+ GameServiceProviderConfiguration configuration) {
+ when(mMockGameServiceProviderSelector.get(user)).thenReturn(configuration);
+ FakeGameServiceProviderInstance instanceForConfiguration =
+ spy(new FakeGameServiceProviderInstance());
+ when(mMockGameServiceProviderInstanceFactory.create(configuration))
+ .thenReturn(instanceForConfiguration);
+
+ return instanceForConfiguration;
+ }
+
+ private static SystemService.TargetUser user(int userId) {
+ UserInfo userInfo = new UserInfo(userId, "", "", UserInfo.FLAG_FULL);
+ return new SystemService.TargetUser(userInfo);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTests.java
deleted file mode 100644
index 8973a897e888..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTests.java
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.app;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
-import android.service.games.GameService;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.SystemService;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoSession;
-
-import java.util.List;
-
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public final class GameServiceControllerTests {
- @Mock
- private PackageManager mMockPackageManager;
- @Mock
- private Resources mMockResources;
- @Mock
- private Context mMockContext;
- private MockitoSession mMockingSession;
-
- private static UserInfo eligibleUserInfo(int uid) {
- return new UserInfo(uid, "", "", UserInfo.FLAG_FULL);
- }
-
- private static UserInfo managedUserInfo(int uid) {
- UserInfo userInfo = eligibleUserInfo(uid);
- userInfo.userType = UserManager.USER_TYPE_PROFILE_MANAGED;
- return userInfo;
- }
-
- private static ResolveInfo resolveInfo(ServiceInfo serviceInfo) {
- ResolveInfo resolveInfo = new ResolveInfo();
- resolveInfo.serviceInfo = serviceInfo;
- return resolveInfo;
- }
-
- private static ServiceInfo serviceInfo(String packageName, String name, boolean isEnabled) {
- ApplicationInfo applicationInfo = new ApplicationInfo();
- applicationInfo.packageName = packageName;
- applicationInfo.enabled = true;
-
- ServiceInfo serviceInfo = new ServiceInfo();
- serviceInfo.applicationInfo = applicationInfo;
- serviceInfo.packageName = packageName;
- serviceInfo.name = name;
- serviceInfo.enabled = isEnabled;
- return serviceInfo;
- }
-
- private static SystemService.TargetUser managedTargetUser(int ineligibleUserId) {
- return new SystemService.TargetUser(managedUserInfo(ineligibleUserId));
- }
-
- private static SystemService.TargetUser eligibleTargetUser(int userId) {
- return new SystemService.TargetUser(eligibleUserInfo(userId));
- }
-
- private static UserHandle userWithId(int userId) {
- return argThat(userInfo -> userInfo.getIdentifier() == userId);
- }
-
- @Before
- public void setUp() {
- mMockingSession = mockitoSession()
- .initMocks(this)
- .startMocking();
- }
-
- @After
- public void tearDown() {
- mMockingSession.finishMocking();
- }
-
- @Test
- public void testStartConnectionOnBootWithNoUser() {
- GameServiceController gameServiceController =
- new GameServiceController(mMockContext);
-
- gameServiceController.onBootComplete();
-
- verifyNoServiceBound();
- }
-
- @Test
- public void testStartConnectionOnBootWithManagedUser() {
- int userId = 12345;
- GameServiceController gameServiceController =
- new GameServiceController(mMockContext);
-
- gameServiceController.notifyUserStarted(managedTargetUser(userId));
- gameServiceController.onBootComplete();
-
- verifyNoServiceBound();
- }
-
- @Test
- public void testStartConnectionOnBootWithUserAndNoSystemGamesServiceSet() {
- seedSystemGameServicePackageName("");
-
- GameServiceController gameServiceController =
- new GameServiceController(mMockContext);
-
- gameServiceController.notifyUserStarted(eligibleTargetUser(1000));
- gameServiceController.onBootComplete();
-
- verifyNoServiceBound();
- }
-
- @Test
- public void testStartConnectionOnBootWithUserAndSystemGamesServiceDoesNotExist() {
- int userId = 12345;
- String gameServicePackageName = "game.service.package";
- seedSystemGameServicePackageName(gameServicePackageName);
- seedGameServiceResolveInfos(gameServicePackageName, userId, ImmutableList.of());
-
- GameServiceController gameServiceController =
- new GameServiceController(mMockContext);
-
- gameServiceController.notifyUserStarted(eligibleTargetUser(userId));
- gameServiceController.onBootComplete();
-
- verifyNoServiceBound();
- }
-
- @Test
- public void testStartConnectionOnBootWithUserAndSystemGamesServiceSet() {
- int userId = 12345;
- String gameServicePackageName = "game.service.package";
- String gameServiceComponent = "game.service.package.example.GameService";
- seedSystemGameServicePackageName(gameServicePackageName);
- seedGameServiceResolveInfos(gameServicePackageName, userId, ImmutableList.of(
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent, true))));
-
- GameServiceController gameServiceController =
- new GameServiceController(mMockContext);
-
- gameServiceController.notifyUserStarted(eligibleTargetUser(userId));
- gameServiceController.onBootComplete();
-
- verifyServiceBoundForUserAndComponent(userId, gameServicePackageName, gameServiceComponent);
- }
-
- @Test
- public void testStartConnectionOnBootWithUserAndSystemGamesServiceNotEnabled() {
- int userId = 12345;
- String gameServicePackageName = "game.service.package";
- String gameServiceComponent = "game.service.package.example.GameService";
- seedSystemGameServicePackageName(gameServicePackageName);
- seedGameServiceResolveInfos(gameServicePackageName, userId, ImmutableList.of(
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent, false))));
-
- GameServiceController gameServiceController =
- new GameServiceController(mMockContext);
-
- gameServiceController.notifyUserStarted(eligibleTargetUser(userId));
- gameServiceController.onBootComplete();
-
- verifyNoServiceBound();
- }
-
- @Test
- public void testStartConnectionOnBootWithUserAndSystemGamesServiceHasMultipleComponents() {
- int userId = 12345;
- String gameServicePackageName = "game.service.package";
- String gameServiceComponent1 = "game.service.package.example.GameService1";
- String gameServiceComponent2 = "game.service.package.example.GameService2";
- seedSystemGameServicePackageName(gameServicePackageName);
- seedGameServiceResolveInfos(gameServicePackageName, userId, ImmutableList.of(
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent1, true)),
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent2, true))));
-
- GameServiceController gameServiceController =
- new GameServiceController(mMockContext);
-
- gameServiceController.notifyUserStarted(eligibleTargetUser(userId));
- gameServiceController.onBootComplete();
-
- verifyServiceBoundForUserAndComponent(userId, gameServicePackageName,
- gameServiceComponent1);
- }
-
- @Test
- public void testStartConnectionOnBootWithUserAndSystemGamesServiceHasDisabledComponent() {
- int userId = 12345;
- String gameServicePackageName = "game.service.package";
- String gameServiceComponent1 = "game.service.package.example.GameService1";
- String gameServiceComponent2 = "game.service.package.example.GameService2";
- seedSystemGameServicePackageName(gameServicePackageName);
- seedGameServiceResolveInfos(gameServicePackageName, userId, ImmutableList.of(
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent1, false)),
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent2, true))));
-
- GameServiceController gameServiceController =
- new GameServiceController(mMockContext);
-
- gameServiceController.notifyUserStarted(eligibleTargetUser(userId));
- gameServiceController.onBootComplete();
-
- verifyServiceBoundForUserAndComponent(userId, gameServicePackageName,
- gameServiceComponent2);
- }
-
- @Test
- public void testSwitchFromEligibleUserToEligibleUser() {
- int userId1 = 1;
- int userId2 = 2;
- String gameServicePackageName = "game.service.package";
- String gameServiceComponent = "game.service.package.example.GameService";
- seedSystemGameServicePackageName(gameServicePackageName);
- seedGameServiceResolveInfos(gameServicePackageName, userId1, ImmutableList.of(
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent, true))));
- seedGameServiceResolveInfos(gameServicePackageName, userId2, ImmutableList.of(
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent, true))));
- seedGameServiceToBindSuccessfully();
-
- GameServiceController gameServiceController = new GameServiceController(mMockContext);
-
- gameServiceController.onBootComplete();
- gameServiceController.notifyUserStarted(eligibleTargetUser(userId1));
-
- verifyServiceBoundForUserAndComponent(userId1, gameServicePackageName,
- gameServiceComponent);
-
- gameServiceController.notifyNewForegroundUser(eligibleTargetUser(userId2));
-
- verify(mMockContext).unbindService(any());
- verifyServiceBoundForUserAndComponent(userId2, gameServicePackageName,
- gameServiceComponent);
- }
-
- @Test
- public void testSwitchFromEligibleUserToIneligibleUser() {
- int eligibleUserId = 1;
- int ineligibleUserId = 2;
- String gameServicePackageName = "game.service.package";
- String gameServiceComponent = "game.service.package.example.GameService";
- seedSystemGameServicePackageName(gameServicePackageName);
- seedGameServiceResolveInfos(gameServicePackageName, eligibleUserId, ImmutableList.of(
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent, true))));
- seedGameServiceToBindSuccessfully();
-
- GameServiceController gameServiceController =
- new GameServiceController(mMockContext);
-
- gameServiceController.onBootComplete();
- gameServiceController.notifyUserStarted(eligibleTargetUser(eligibleUserId));
-
- verifyServiceBoundForUserAndComponent(eligibleUserId, gameServicePackageName,
- gameServiceComponent);
-
- gameServiceController.notifyNewForegroundUser(managedTargetUser(ineligibleUserId));
-
- verify(mMockContext).unbindService(any());
- }
-
- @Test
- public void testSwitchFromIneligibleUserToEligibleUser() {
- int eligibleUserId = 1;
- int ineligibleUserId = 2;
- String gameServicePackageName = "game.service.package";
- String gameServiceComponent = "game.service.package.example.GameService";
- seedSystemGameServicePackageName(gameServicePackageName);
- seedGameServiceResolveInfos(gameServicePackageName, eligibleUserId, ImmutableList.of(
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent, true))));
- seedGameServiceToBindSuccessfully();
-
- GameServiceController gameServiceController = new GameServiceController(mMockContext);
-
- gameServiceController.onBootComplete();
- gameServiceController.notifyUserStarted(managedTargetUser(ineligibleUserId));
-
- verifyNoServiceBound();
-
- gameServiceController.notifyNewForegroundUser(eligibleTargetUser(eligibleUserId));
-
- verifyServiceBoundForUserAndComponent(eligibleUserId, gameServicePackageName,
- gameServiceComponent);
- }
-
- @Test
- public void testMultipleRunningUsers() {
- int userId1 = 123;
- int userId2 = 456;
- String gameServicePackageName = "game.service.package";
- String gameServiceComponent = "game.service.package.example.GameService";
- seedSystemGameServicePackageName(gameServicePackageName);
- seedGameServiceResolveInfos(gameServicePackageName, userId1, ImmutableList.of(
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent, true))));
- seedGameServiceToBindSuccessfully();
-
- GameServiceController gameServiceController =
- new GameServiceController(mMockContext);
-
- gameServiceController.onBootComplete();
- gameServiceController.notifyUserStarted(eligibleTargetUser(userId1));
- gameServiceController.notifyUserStarted(eligibleTargetUser(userId2));
-
- verifyServiceBoundForUserAndComponent(userId1, gameServicePackageName,
- gameServiceComponent);
- verifyServiceNotBoundForUser(userId2);
- verify(mMockContext, never()).unbindService(any());
- }
-
- @Test
- public void testForegroundUserStopped() {
- int userId = 123123;
- String gameServicePackageName = "game.service.package";
- String gameServiceComponent = "game.service.package.example.GameService";
- seedSystemGameServicePackageName(gameServicePackageName);
- seedGameServiceResolveInfos(gameServicePackageName, userId, ImmutableList.of(
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent, true))));
- seedGameServiceToBindSuccessfully();
-
- GameServiceController gameServiceController =
- new GameServiceController(mMockContext);
-
- gameServiceController.onBootComplete();
- gameServiceController.notifyUserStarted(eligibleTargetUser(userId));
-
- verifyServiceBoundForUserAndComponent(userId, gameServicePackageName, gameServiceComponent);
-
- gameServiceController.notifyUserStopped(eligibleTargetUser(userId));
-
- verify(mMockContext).unbindService(any());
- }
-
- @Test
- public void testNonForegroundUserStopped() {
- int userId1 = 123;
- int userId2 = 456;
- String gameServicePackageName = "game.service.package";
- String gameServiceComponent = "game.service.package.example.GameService";
- seedSystemGameServicePackageName(gameServicePackageName);
- seedGameServiceResolveInfos(gameServicePackageName, userId1, ImmutableList.of(
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent, true))));
- seedGameServiceResolveInfos(gameServicePackageName, userId2, ImmutableList.of(
- resolveInfo(serviceInfo(gameServicePackageName, gameServiceComponent, true))));
- seedGameServiceToBindSuccessfully();
-
- GameServiceController gameServiceController =
- new GameServiceController(mMockContext);
- InOrder inOrder = Mockito.inOrder(mMockContext);
-
- gameServiceController.onBootComplete();
- gameServiceController.notifyUserStarted(eligibleTargetUser(userId1));
-
- inOrder.verify(mMockContext).bindServiceAsUser(any(), any(), anyInt(), userWithId(userId1));
-
- gameServiceController.notifyNewForegroundUser(eligibleTargetUser(userId2));
-
- inOrder.verify(mMockContext).unbindService(any());
- inOrder.verify(mMockContext).bindServiceAsUser(any(), any(), anyInt(), userWithId(userId2));
-
- gameServiceController.notifyUserStopped(eligibleTargetUser(userId1));
-
- inOrder.verify(mMockContext, never()).unbindService(any());
- }
-
- private void seedSystemGameServicePackageName(String gameServicePackageName) {
- when(mMockContext.getResources()).thenReturn(mMockResources);
- when(mMockResources.getString(com.android.internal.R.string.config_systemGameService))
- .thenReturn(gameServicePackageName);
- }
-
- private void seedGameServiceResolveInfos(String gameServicePackageName, int userId,
- List<ResolveInfo> resolveInfos) {
- when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
- doReturn(resolveInfos)
- .when(mMockPackageManager).queryIntentServicesAsUser(
- argThat(intent ->
- intent != null
- && intent.getAction().equals(GameService.SERVICE_INTERFACE)
- && intent.getPackage().equals(gameServicePackageName)
- ),
- eq(PackageManager.MATCH_SYSTEM_ONLY),
- eq(userId));
- }
-
- private void seedGameServiceToBindSuccessfully() {
- when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
- }
-
- private void verifyNoServiceBound() {
- verify(mMockContext, never()).bindServiceAsUser(any(), any(), anyInt(), any());
- }
-
- private void verifyServiceBoundForUserAndComponent(int userId, String gameServicePackageName,
- String gameServiceComponent) {
- verify(mMockContext).bindServiceAsUser(
- argThat(intent -> intent.getAction().equals(GameService.SERVICE_INTERFACE)
- && intent.getComponent().getPackageName().equals(gameServicePackageName)
- && intent.getComponent().getClassName().equals(gameServiceComponent)),
- any(),
- anyInt(), argThat(userInfo -> userInfo.getIdentifier() == userId));
- }
-
- private void verifyServiceNotBoundForUser(int userId) {
- verify(mMockContext, never()).bindServiceAsUser(
- any(),
- any(),
- anyInt(), userWithId(userId));
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
new file mode 100644
index 000000000000..b6c706ed2730
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -0,0 +1,560 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+
+import android.annotation.Nullable;
+import android.app.IActivityTaskManager;
+import android.app.ITaskStackListener;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.service.games.CreateGameSessionRequest;
+import android.service.games.IGameService;
+import android.service.games.IGameSession;
+import android.service.games.IGameSessionService;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.util.ConcurrentUtils;
+import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+
+/**
+ * Unit tests for the {@link GameServiceProviderInstanceImpl}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public final class GameServiceProviderInstanceImplTest {
+
+ private static final int USER_ID = 10;
+ private static final String APP_A_PACKAGE = "com.package.app.a";
+ private static final ComponentName APP_A_MAIN_ACTIVITY =
+ new ComponentName(APP_A_PACKAGE, "com.package.app.a.MainActivity");
+
+ private static final String GAME_A_PACKAGE = "com.package.game.a";
+ private static final ComponentName GAME_A_MAIN_ACTIVITY =
+ new ComponentName(GAME_A_PACKAGE, "com.package.game.a.MainActivity");
+
+ private MockitoSession mMockingSession;
+ private GameServiceProviderInstance mGameServiceProviderInstance;
+ @Mock
+ private IActivityTaskManager mMockActivityTaskManager;
+ @Mock
+ private IGameService mMockGameService;
+ @Mock
+ private IGameSessionService mMockGameSessionService;
+ private FakeGameClassifier mFakeGameClassifier;
+ private FakeServiceConnector<IGameService> mFakeGameServiceConnector;
+ private FakeServiceConnector<IGameSessionService> mFakeGameSessionServiceConnector;
+ private ArrayList<ITaskStackListener> mTaskStackListeners;
+ private InOrder mInOrder;
+
+ @Before
+ public void setUp() throws PackageManager.NameNotFoundException, RemoteException {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ mInOrder = inOrder(mMockGameService, mMockGameSessionService);
+
+ mFakeGameClassifier = new FakeGameClassifier();
+ mFakeGameClassifier.recordGamePackage(GAME_A_PACKAGE);
+
+ mFakeGameServiceConnector = new FakeServiceConnector<>(mMockGameService);
+ mFakeGameSessionServiceConnector = new FakeServiceConnector<>(mMockGameSessionService);
+
+ mTaskStackListeners = new ArrayList<>();
+ doAnswer(invocation -> {
+ mTaskStackListeners.add(invocation.getArgument(0));
+ return null;
+ }).when(mMockActivityTaskManager).registerTaskStackListener(any());
+
+ doAnswer(invocation -> {
+ mTaskStackListeners.remove(invocation.getArgument(0));
+ return null;
+ }).when(mMockActivityTaskManager).unregisterTaskStackListener(any());
+
+ mGameServiceProviderInstance = new GameServiceProviderInstanceImpl(
+ new UserHandle(USER_ID),
+ ConcurrentUtils.DIRECT_EXECUTOR,
+ mFakeGameClassifier,
+ mMockActivityTaskManager,
+ mFakeGameServiceConnector,
+ mFakeGameSessionServiceConnector);
+ }
+
+ @After
+ public void tearDown() {
+ mMockingSession.finishMocking();
+ }
+
+ @Test
+ public void start_startsGameSession() throws Exception {
+ mGameServiceProviderInstance.start();
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void start_multipleTimes_startsGameSessionOnce() throws Exception {
+ mGameServiceProviderInstance.start();
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void stop_neverStarted_doesNothing() throws Exception {
+ mGameServiceProviderInstance.stop();
+
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(0);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ mInOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void startAndStop_startsAndStopsGameSession() throws Exception {
+ mGameServiceProviderInstance.start();
+ mGameServiceProviderInstance.stop();
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).disconnected();
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void startAndStop_multipleTimes_startsAndStopsGameSessionMultipleTimes()
+ throws Exception {
+ mGameServiceProviderInstance.start();
+ mGameServiceProviderInstance.stop();
+ mGameServiceProviderInstance.start();
+ mGameServiceProviderInstance.stop();
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).disconnected();
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).disconnected();
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(2);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void stop_stopMultipleTimes_stopsGameSessionOnce() throws Exception {
+ mGameServiceProviderInstance.start();
+ mGameServiceProviderInstance.stop();
+ mGameServiceProviderInstance.stop();
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).disconnected();
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void gameTaskStarted_neverStarted_doesNothing() throws Exception {
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(0);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void gameTaskRemoved_neverStarted_doesNothing() throws Exception {
+ dispatchTaskRemoved(10);
+
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(0);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void gameTaskStarted_afterStopped_doesNothing() throws Exception {
+ mGameServiceProviderInstance.start();
+ mGameServiceProviderInstance.stop();
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).disconnected();
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void appTaskStarted_doesNothing() throws Exception {
+ mGameServiceProviderInstance.start();
+ dispatchTaskCreated(10, APP_A_MAIN_ACTIVITY);
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void taskStarted_nullComponentName_ignoresAndDoesNotCrash() throws Exception {
+ mGameServiceProviderInstance.start();
+ dispatchTaskCreated(10, null);
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void gameTaskStarted_createsGameSession() throws Exception {
+ CreateGameSessionRequest createGameSessionRequest =
+ new CreateGameSessionRequest(10, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession10Future =
+ captureCreateGameSessionFuture(createGameSessionRequest);
+
+ mGameServiceProviderInstance.start();
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession10 = new IGameSessionStub();
+ gameSession10Future.get().complete(gameSession10);
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(gameSession10.mIsDestroyed).isFalse();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void gameTaskRemoved_whileAwaitingGameSessionAttached_destroysGameSession()
+ throws Exception {
+ CreateGameSessionRequest createGameSessionRequest =
+ new CreateGameSessionRequest(10, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession10Future =
+ captureCreateGameSessionFuture(createGameSessionRequest);
+
+ mGameServiceProviderInstance.start();
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ dispatchTaskRemoved(10);
+ IGameSessionStub gameSession10 = new IGameSessionStub();
+ gameSession10Future.get().complete(gameSession10);
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(gameSession10.mIsDestroyed).isTrue();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse();
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void gameTaskRemoved_destroysGameSession() throws Exception {
+ CreateGameSessionRequest createGameSessionRequest =
+ new CreateGameSessionRequest(10, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession10Future =
+ captureCreateGameSessionFuture(createGameSessionRequest);
+
+ mGameServiceProviderInstance.start();
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession10 = new IGameSessionStub();
+ gameSession10Future.get().complete(gameSession10);
+ dispatchTaskRemoved(10);
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(gameSession10.mIsDestroyed).isTrue();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse();
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void gameTaskStarted_multipleTimes_createsMultipleGameSessions() throws Exception {
+ CreateGameSessionRequest createGameSessionRequest10 =
+ new CreateGameSessionRequest(10, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession10Future =
+ captureCreateGameSessionFuture(createGameSessionRequest10);
+
+ CreateGameSessionRequest createGameSessionRequest11 =
+ new CreateGameSessionRequest(11, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession11Future =
+ captureCreateGameSessionFuture(createGameSessionRequest11);
+
+ mGameServiceProviderInstance.start();
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession10 = new IGameSessionStub();
+ gameSession10Future.get().complete(gameSession10);
+
+ dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession11 = new IGameSessionStub();
+ gameSession11Future.get().complete(gameSession11);
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(gameSession10.mIsDestroyed).isFalse();
+ assertThat(gameSession11.mIsDestroyed).isFalse();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void gameTaskRemoved_afterMultipleCreated_destroysOnlyThatGameSession()
+ throws Exception {
+ CreateGameSessionRequest createGameSessionRequest10 =
+ new CreateGameSessionRequest(10, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession10Future =
+ captureCreateGameSessionFuture(createGameSessionRequest10);
+
+ CreateGameSessionRequest createGameSessionRequest11 =
+ new CreateGameSessionRequest(11, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession11Future =
+ captureCreateGameSessionFuture(createGameSessionRequest11);
+
+ mGameServiceProviderInstance.start();
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession10 = new IGameSessionStub();
+ gameSession10Future.get().complete(gameSession10);
+
+ dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession11 = new IGameSessionStub();
+ gameSession11Future.get().complete(gameSession11);
+
+ dispatchTaskRemoved(10);
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(gameSession10.mIsDestroyed).isTrue();
+ assertThat(gameSession11.mIsDestroyed).isFalse();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void allGameTasksRemoved_destroysAllGameSessions() throws Exception {
+ CreateGameSessionRequest createGameSessionRequest10 =
+ new CreateGameSessionRequest(10, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession10Future =
+ captureCreateGameSessionFuture(createGameSessionRequest10);
+
+ CreateGameSessionRequest createGameSessionRequest11 =
+ new CreateGameSessionRequest(11, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession11Future =
+ captureCreateGameSessionFuture(createGameSessionRequest11);
+
+ mGameServiceProviderInstance.start();
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession10 = new IGameSessionStub();
+ gameSession10Future.get().complete(gameSession10);
+
+ dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession11 = new IGameSessionStub();
+ gameSession11Future.get().complete(gameSession11);
+
+ dispatchTaskRemoved(10);
+ dispatchTaskRemoved(11);
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(gameSession10.mIsDestroyed).isTrue();
+ assertThat(gameSession11.mIsDestroyed).isTrue();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse();
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void gameTasksCreated_afterAllPreviousSessionsDestroyed_createsSession()
+ throws Exception {
+ CreateGameSessionRequest createGameSessionRequest10 =
+ new CreateGameSessionRequest(10, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession10Future =
+ captureCreateGameSessionFuture(createGameSessionRequest10);
+
+ CreateGameSessionRequest createGameSessionRequest11 =
+ new CreateGameSessionRequest(11, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession11Future =
+ captureCreateGameSessionFuture(createGameSessionRequest11);
+
+ CreateGameSessionRequest createGameSessionRequest12 =
+ new CreateGameSessionRequest(12, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> unusedGameSession12Future =
+ captureCreateGameSessionFuture(createGameSessionRequest12);
+
+ mGameServiceProviderInstance.start();
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession10 = new IGameSessionStub();
+ gameSession10Future.get().complete(gameSession10);
+
+ dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession11 = new IGameSessionStub();
+ gameSession11Future.get().complete(gameSession11);
+
+ dispatchTaskRemoved(10);
+ dispatchTaskRemoved(11);
+
+ dispatchTaskCreated(12, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession12 = new IGameSessionStub();
+ gameSession11Future.get().complete(gameSession12);
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest12), any());
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(gameSession10.mIsDestroyed).isTrue();
+ assertThat(gameSession11.mIsDestroyed).isTrue();
+ assertThat(gameSession12.mIsDestroyed).isFalse();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(2);
+ }
+
+ @Test
+ public void stop_severalActiveGameSessions_destroysGameSessionsAndUnbinds() throws Exception {
+ CreateGameSessionRequest createGameSessionRequest10 =
+ new CreateGameSessionRequest(10, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession10Future =
+ captureCreateGameSessionFuture(createGameSessionRequest10);
+
+ CreateGameSessionRequest createGameSessionRequest11 =
+ new CreateGameSessionRequest(11, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession11Future =
+ captureCreateGameSessionFuture(createGameSessionRequest11);
+
+ mGameServiceProviderInstance.start();
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession10 = new IGameSessionStub();
+ gameSession10Future.get().complete(gameSession10);
+ dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
+ IGameSessionStub gameSession11 = new IGameSessionStub();
+ gameSession11Future.get().complete(gameSession11);
+ mGameServiceProviderInstance.stop();
+
+ mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
+ mInOrder.verify(mMockGameService).disconnected();
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(gameSession10.mIsDestroyed).isTrue();
+ assertThat(gameSession11.mIsDestroyed).isTrue();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse();
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ }
+
+ private Supplier<AndroidFuture<IBinder>> captureCreateGameSessionFuture(
+ CreateGameSessionRequest expectedCreateGameSessionRequest) throws Exception {
+ final AtomicReference<AndroidFuture<IBinder>> gameSessionFuture = new AtomicReference<>();
+ doAnswer(invocation -> {
+ gameSessionFuture.set(invocation.getArgument(1));
+ return null;
+ }).when(mMockGameSessionService).create(eq(expectedCreateGameSessionRequest), any());
+
+ return gameSessionFuture::get;
+ }
+
+ private void dispatchTaskRemoved(int taskId) {
+ dispatchTaskChangeEvent(taskStackListener -> {
+ taskStackListener.onTaskRemoved(taskId);
+ });
+ }
+
+ private void dispatchTaskCreated(int taskId, @Nullable ComponentName componentName) {
+ dispatchTaskChangeEvent(taskStackListener -> {
+ taskStackListener.onTaskCreated(taskId, componentName);
+ });
+ }
+
+ private void dispatchTaskChangeEvent(
+ ThrowingConsumer<ITaskStackListener> taskStackListenerConsumer) {
+ for (ITaskStackListener taskStackListener : mTaskStackListeners) {
+ taskStackListenerConsumer.accept(taskStackListener);
+ }
+ }
+
+ private static class IGameSessionStub extends IGameSession.Stub {
+ boolean mIsDestroyed = false;
+
+ @Override
+ public void destroy() {
+ mIsDestroyed = true;
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java
new file mode 100644
index 000000000000..59d0970f5934
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.service.games.GameService;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.SystemService;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+
+/**
+ * Unit tests for the {@link GameServiceProviderSelectorImpl}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public final class GameServiceProviderSelectorImplTest {
+
+ private static final UserHandle USER_HANDLE_10 = new UserHandle(10);
+
+ private static final int GAME_SERVICE_META_DATA_RES_ID = 1337;
+ private static final String GAME_SERVICE_PACKAGE_NAME = "com.game.service.provider";
+ private static final String GAME_SERVICE_CLASS_NAME = "com.game.service.provider.GameService";
+ private static final ComponentName GAME_SERVICE_COMPONENT =
+ new ComponentName(GAME_SERVICE_PACKAGE_NAME, GAME_SERVICE_CLASS_NAME);
+
+ private static final int GAME_SERVICE_B_META_DATA_RES_ID = 1338;
+ private static final String GAME_SERVICE_B_CLASS_NAME =
+ "com.game.service.provider.GameServiceB";
+ private static final ComponentName GAME_SERVICE_B_COMPONENT =
+ new ComponentName(GAME_SERVICE_PACKAGE_NAME, GAME_SERVICE_B_CLASS_NAME);
+ private static final ServiceInfo GAME_SERVICE_B_WITH_OUT_META_DATA =
+ serviceInfo(GAME_SERVICE_PACKAGE_NAME, GAME_SERVICE_B_CLASS_NAME);
+ private static final ServiceInfo GAME_SERVICE_B_SERVICE_INFO =
+ addGameServiceMetaData(GAME_SERVICE_B_WITH_OUT_META_DATA,
+ GAME_SERVICE_B_META_DATA_RES_ID);
+
+ private static final String GAME_SESSION_SERVICE_CLASS_NAME =
+ "com.game.service.provider.GameSessionService";
+ private static final ComponentName GAME_SESSION_SERVICE_COMPONENT =
+ new ComponentName(GAME_SERVICE_PACKAGE_NAME, GAME_SESSION_SERVICE_CLASS_NAME);
+ private static final ServiceInfo GAME_SERVICE_SERVICE_INFO_WITHOUT_META_DATA =
+ serviceInfo(GAME_SERVICE_PACKAGE_NAME, GAME_SERVICE_CLASS_NAME);
+ private static final ServiceInfo GAME_SERVICE_SERVICE_INFO =
+ addGameServiceMetaData(GAME_SERVICE_SERVICE_INFO_WITHOUT_META_DATA,
+ GAME_SERVICE_META_DATA_RES_ID);
+
+ @Mock
+ private PackageManager mMockPackageManager;
+ private Resources mSpyResources;
+ private MockitoSession mMockingSession;
+ private GameServiceProviderSelector mGameServiceProviderSelector;
+
+ @Before
+ public void setUp() throws PackageManager.NameNotFoundException {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ mSpyResources = spy(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources());
+
+ when(mMockPackageManager.getResourcesForApplication(anyString()))
+ .thenReturn(mSpyResources);
+ mGameServiceProviderSelector = new GameServiceProviderSelectorImpl(
+ mSpyResources,
+ mMockPackageManager);
+ }
+
+ @After
+ public void tearDown() {
+ mMockingSession.finishMocking();
+ }
+
+ @Test
+ public void get_nullUser_returnsNull()
+ throws Exception {
+ seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
+ seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
+ resolveInfo(GAME_SERVICE_SERVICE_INFO));
+ seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
+ seedGameServiceMetaDataFromFile(GAME_SERVICE_PACKAGE_NAME,
+ GAME_SERVICE_META_DATA_RES_ID,
+ "res/xml/game_service_metadata_valid.xml");
+
+ GameServiceProviderConfiguration gameServiceProviderConfiguration =
+ mGameServiceProviderSelector.get(null);
+
+ assertThat(gameServiceProviderConfiguration).isNull();
+ }
+
+ @Test
+ public void get_managedUser_returnsNull()
+ throws Exception {
+ seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
+ seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
+ resolveInfo(GAME_SERVICE_SERVICE_INFO));
+ seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
+ seedGameServiceMetaDataFromFile(GAME_SERVICE_PACKAGE_NAME,
+ GAME_SERVICE_META_DATA_RES_ID,
+ "res/xml/game_service_metadata_valid.xml");
+
+ GameServiceProviderConfiguration gameServiceProviderConfiguration =
+ mGameServiceProviderSelector.get(managedTargetUser(USER_HANDLE_10));
+
+ assertThat(gameServiceProviderConfiguration).isNull();
+ }
+
+ @Test
+ public void get_noSystemGameService_returnsNull()
+ throws Exception {
+ seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
+ resolveInfo(GAME_SERVICE_SERVICE_INFO));
+ seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
+ seedGameServiceMetaDataFromFile(GAME_SERVICE_PACKAGE_NAME,
+ GAME_SERVICE_META_DATA_RES_ID,
+ "res/xml/game_service_metadata_valid.xml");
+
+ GameServiceProviderConfiguration gameServiceProviderConfiguration =
+ mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+
+ assertThat(gameServiceProviderConfiguration).isNull();
+ }
+
+ @Test
+ public void get_noGameServiceProvidersAvailable_returnsNull()
+ throws Exception {
+ seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
+ seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10);
+ seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
+ seedGameServiceMetaDataFromFile(GAME_SERVICE_PACKAGE_NAME,
+ GAME_SERVICE_META_DATA_RES_ID,
+ "res/xml/game_service_metadata_valid.xml");
+
+ GameServiceProviderConfiguration gameServiceProviderConfiguration =
+ mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+
+ assertThat(gameServiceProviderConfiguration).isNull();
+ }
+
+ @Test
+ public void get_gameServiceProviderHasNoMetaData_returnsNull()
+ throws Exception {
+ seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
+ seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
+ resolveInfo(GAME_SERVICE_SERVICE_INFO_WITHOUT_META_DATA));
+ seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
+
+ GameServiceProviderConfiguration gameServiceProviderConfiguration =
+ mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+
+ assertThat(gameServiceProviderConfiguration).isNull();
+ }
+
+ @Test
+ public void get_gameSessionServiceDoesNotExist_returnsNull()
+ throws Exception {
+ seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
+ seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
+ resolveInfo(GAME_SERVICE_SERVICE_INFO));
+ seedServiceServiceInfoNotFound(GAME_SESSION_SERVICE_COMPONENT);
+ seedGameServiceMetaDataFromFile(GAME_SERVICE_PACKAGE_NAME,
+ GAME_SERVICE_META_DATA_RES_ID,
+ "res/xml/game_service_metadata_valid.xml");
+
+ GameServiceProviderConfiguration gameServiceProviderConfiguration =
+ mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+
+ assertThat(gameServiceProviderConfiguration).isNull();
+ }
+
+ @Test
+ public void get_metaDataWrongFirstTag_returnsNull() throws Exception {
+ seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
+ seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
+ resolveInfo(GAME_SERVICE_SERVICE_INFO));
+ seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
+ seedGameServiceMetaDataFromFile(GAME_SERVICE_PACKAGE_NAME,
+ GAME_SERVICE_META_DATA_RES_ID,
+ "res/xml/game_service_metadata_wrong_first_tag.xml");
+
+ GameServiceProviderConfiguration gameServiceProviderConfiguration =
+ mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+
+ assertThat(gameServiceProviderConfiguration).isNull();
+ }
+
+ @Test
+ public void get_validGameServiceProviderAvailable_returnsGameServiceProvider()
+ throws Exception {
+ seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
+ seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
+ resolveInfo(GAME_SERVICE_SERVICE_INFO));
+ seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
+ seedGameServiceMetaDataFromFile(GAME_SERVICE_PACKAGE_NAME,
+ GAME_SERVICE_META_DATA_RES_ID,
+ "res/xml/game_service_metadata_valid.xml");
+
+ GameServiceProviderConfiguration gameServiceProviderConfiguration =
+ mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+
+ GameServiceProviderConfiguration expectedGameServiceProviderConfiguration =
+ new GameServiceProviderConfiguration(USER_HANDLE_10,
+ GAME_SERVICE_COMPONENT,
+ GAME_SESSION_SERVICE_COMPONENT);
+ assertThat(gameServiceProviderConfiguration).isEqualTo(
+ expectedGameServiceProviderConfiguration);
+ }
+
+ @Test
+ public void get_multipleGameServiceProvidersAllValid_returnsFirstValidGameServiceProvider()
+ throws Exception {
+ seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
+
+ seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
+ resolveInfo(GAME_SERVICE_B_SERVICE_INFO), resolveInfo(GAME_SERVICE_SERVICE_INFO));
+ seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
+ seedGameServiceMetaDataFromFile(GAME_SERVICE_PACKAGE_NAME,
+ GAME_SERVICE_B_META_DATA_RES_ID,
+ "res/xml/game_service_metadata_valid.xml");
+ seedGameServiceMetaDataFromFile(GAME_SERVICE_PACKAGE_NAME,
+ GAME_SERVICE_META_DATA_RES_ID,
+ "res/xml/game_service_metadata_valid.xml");
+
+ GameServiceProviderConfiguration gameServiceProviderConfiguration =
+ mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+
+ GameServiceProviderConfiguration expectedGameServiceProviderConfiguration =
+ new GameServiceProviderConfiguration(USER_HANDLE_10,
+ GAME_SERVICE_B_COMPONENT,
+ GAME_SESSION_SERVICE_COMPONENT);
+ assertThat(gameServiceProviderConfiguration).isEqualTo(
+ expectedGameServiceProviderConfiguration);
+ }
+
+ @Test
+ public void get_multipleGameServiceProvidersSomeInvalid_returnsFirstValidGameServiceProvider()
+ throws Exception {
+ seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
+
+ seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
+ resolveInfo(GAME_SERVICE_B_SERVICE_INFO), resolveInfo(GAME_SERVICE_SERVICE_INFO));
+ seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
+ seedGameServiceMetaDataFromFile(GAME_SERVICE_PACKAGE_NAME,
+ GAME_SERVICE_META_DATA_RES_ID,
+ "res/xml/game_service_metadata_valid.xml");
+
+ GameServiceProviderConfiguration gameServiceProviderConfiguration =
+ mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+
+ GameServiceProviderConfiguration expectedGameServiceProviderConfiguration =
+ new GameServiceProviderConfiguration(USER_HANDLE_10,
+ GAME_SERVICE_COMPONENT,
+ GAME_SESSION_SERVICE_COMPONENT);
+ assertThat(gameServiceProviderConfiguration).isEqualTo(
+ expectedGameServiceProviderConfiguration);
+ }
+
+ private void seedSystemGameServicePackageName(String gameServicePackageName) {
+ when(mSpyResources.getString(com.android.internal.R.string.config_systemGameService))
+ .thenReturn(gameServicePackageName);
+ }
+
+ private void seedGameServiceResolveInfos(
+ String gameServicePackageName,
+ UserHandle userHandle,
+ ResolveInfo... resolveInfos) {
+ doReturn(ImmutableList.copyOf(resolveInfos))
+ .when(mMockPackageManager).queryIntentServicesAsUser(
+ argThat(intent ->
+ intent != null
+ && intent.getAction().equals(
+ GameService.ACTION_GAME_SERVICE)
+ && intent.getPackage().equals(gameServicePackageName)
+ ),
+ anyInt(),
+ eq(userHandle.getIdentifier()));
+ }
+
+ private void seedServiceServiceInfo(ComponentName componentName) throws Exception {
+ when(mMockPackageManager.getServiceInfo(eq(componentName), anyInt()))
+ .thenReturn(
+ serviceInfo(componentName.getPackageName(), componentName.getClassName()));
+ }
+
+ private void seedServiceServiceInfoNotFound(ComponentName componentName) throws Exception {
+ when(mMockPackageManager.getServiceInfo(eq(componentName), anyInt()))
+ .thenThrow(new PackageManager.NameNotFoundException());
+ }
+
+ private void seedGameServiceMetaDataFromFile(String packageName, int resId, String fileName)
+ throws Exception {
+
+ AssetManager assetManager =
+ InstrumentationRegistry.getInstrumentation().getContext().getAssets();
+ XmlResourceParser xmlResourceParser =
+ assetManager.openXmlResourceParser(fileName);
+
+ when(mMockPackageManager.getXml(eq(packageName), eq(resId), any()))
+ .thenReturn(xmlResourceParser);
+ }
+
+ private static UserInfo eligibleUserInfo(int uid) {
+ return new UserInfo(uid, "", "", UserInfo.FLAG_FULL);
+ }
+
+ private static UserInfo managedUserInfo(int uid) {
+ UserInfo userInfo = eligibleUserInfo(uid);
+ userInfo.userType = UserManager.USER_TYPE_PROFILE_MANAGED;
+ return userInfo;
+ }
+
+ private static ResolveInfo resolveInfo(ServiceInfo serviceInfo) {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.serviceInfo = serviceInfo;
+ return resolveInfo;
+ }
+
+ private static ServiceInfo serviceInfo(String packageName, String name) {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = packageName;
+ applicationInfo.enabled = true;
+
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.applicationInfo = applicationInfo;
+ serviceInfo.packageName = packageName;
+ serviceInfo.name = name;
+ serviceInfo.enabled = true;
+
+ return serviceInfo;
+ }
+
+ private static ServiceInfo addGameServiceMetaData(ServiceInfo serviceInfo, int resId) {
+ if (serviceInfo.metaData == null) {
+ serviceInfo.metaData = new Bundle();
+ }
+ serviceInfo.metaData.putInt(GameService.SERVICE_META_DATA, resId);
+
+ return serviceInfo;
+ }
+
+ private static SystemService.TargetUser managedTargetUser(UserHandle userHandle) {
+ return new SystemService.TargetUser(managedUserInfo(userHandle.getIdentifier()));
+ }
+
+ private static SystemService.TargetUser eligibleTargetUser(UserHandle userHandle) {
+ return new SystemService.TargetUser(eligibleUserInfo(userHandle.getIdentifier()));
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DensityMapTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DensityMapTest.java
new file mode 100644
index 000000000000..3f69f1b723e3
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DensityMapTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static com.android.server.display.DensityMap.Entry;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DensityMapTest {
+
+ @Test
+ public void testConstructor_withBadConfig_throwsException() {
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(1080, 1920, 320)})
+ );
+
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(1920, 1080, 120)})
+ );
+
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(2160, 3840, 120)})
+ );
+
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(3840, 2160, 120)})
+ );
+
+ // Two entries with the same diagonal
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(500, 500, 123),
+ new Entry(100, 700, 456)})
+ );
+ }
+
+ @Test
+ public void testGetDensityForResolution_withResolutionMatch_returnsDensityFromConfig() {
+ DensityMap densityMap = DensityMap.createByOwning(new Entry[]{
+ new Entry(720, 1280, 213),
+ new Entry(1080, 1920, 320),
+ new Entry(2160, 3840, 640)});
+
+ assertEquals(213, densityMap.getDensityForResolution(720, 1280));
+ assertEquals(213, densityMap.getDensityForResolution(1280, 720));
+
+ assertEquals(320, densityMap.getDensityForResolution(1080, 1920));
+ assertEquals(320, densityMap.getDensityForResolution(1920, 1080));
+
+ assertEquals(640, densityMap.getDensityForResolution(2160, 3840));
+ assertEquals(640, densityMap.getDensityForResolution(3840, 2160));
+ }
+
+ @Test
+ public void testGetDensityForResolution_withDiagonalMatch_returnsDensityFromConfig() {
+ DensityMap densityMap = DensityMap.createByOwning(
+ new Entry[]{ new Entry(500, 500, 123)});
+
+ // 500x500 has the same diagonal as 100x700
+ assertEquals(123, densityMap.getDensityForResolution(100, 700));
+ }
+
+ @Test
+ public void testGetDensityForResolution_withOneEntry_withNoMatch_returnsExtrapolatedDensity() {
+ DensityMap densityMap = DensityMap.createByOwning(
+ new Entry[]{ new Entry(1080, 1920, 320)});
+
+ assertEquals(320, densityMap.getDensityForResolution(1081, 1920));
+ assertEquals(320, densityMap.getDensityForResolution(1080, 1921));
+
+ assertEquals(640, densityMap.getDensityForResolution(2160, 3840));
+ assertEquals(640, densityMap.getDensityForResolution(3840, 2160));
+
+ assertEquals(213, densityMap.getDensityForResolution(720, 1280));
+ assertEquals(213, densityMap.getDensityForResolution(1280, 720));
+ }
+
+ @Test
+ public void testGetDensityForResolution_withTwoEntries_withNoMatch_returnExtrapolatedDensity() {
+ DensityMap densityMap = DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(2160, 3840, 320)});
+
+ // Resolution is smaller than all entries
+ assertEquals(213, densityMap.getDensityForResolution(720, 1280));
+ assertEquals(213, densityMap.getDensityForResolution(1280, 720));
+
+ // Resolution is bigger than all entries
+ assertEquals(320 * 2, densityMap.getDensityForResolution(2160 * 2, 3840 * 2));
+ assertEquals(320 * 2, densityMap.getDensityForResolution(3840 * 2, 2160 * 2));
+ }
+
+ @Test
+ public void testGetDensityForResolution_withNoMatch_returnsInterpolatedDensity() {
+ {
+ DensityMap densityMap = DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(2160, 3840, 320)});
+
+ assertEquals(320, densityMap.getDensityForResolution(2000, 2000));
+ }
+
+ {
+ DensityMap densityMap = DensityMap.createByOwning(new Entry[]{
+ new Entry(720, 1280, 213),
+ new Entry(2160, 3840, 640)});
+
+ assertEquals(320, densityMap.getDensityForResolution(1080, 1920));
+ assertEquals(320, densityMap.getDensityForResolution(1920, 1080));
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index a0d86c9268fb..3d3c1abb3e91 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -1450,11 +1450,10 @@ public class GestureLauncherServiceTest {
}
private void withEmergencyGesturePowerButtonCooldownPeriodMsValue(int period) {
- Settings.Secure.putIntForUser(
+ Settings.Global.putInt(
mContentResolver,
- Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
- period,
- UserHandle.USER_CURRENT);
+ Settings.Global.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
+ period);
}
private void withUserSetupCompleteValue(boolean userSetupComplete) {
diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java b/services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java
new file mode 100644
index 000000000000..7f7901f893a3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.backup.transport;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.backup.BackupTransport;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class TransportStatusCallbackTest {
+ private static final int OPERATION_TIMEOUT_MILLIS = 10;
+ private static final int OPERATION_COMPLETE_STATUS = 123;
+
+ private TransportStatusCallback mTransportStatusCallback;
+
+ @Before
+ public void setUp() {
+ mTransportStatusCallback = new TransportStatusCallback();
+ }
+
+ @Test
+ public void testGetOperationStatus_withPreCompletedOperation_returnsStatus() throws Exception {
+ mTransportStatusCallback.onOperationCompleteWithStatus(OPERATION_COMPLETE_STATUS);
+
+ int result = mTransportStatusCallback.getOperationStatus();
+
+ assertThat(result).isEqualTo(OPERATION_COMPLETE_STATUS);
+ }
+
+ @Test
+ public void testGetOperationStatus_completeOperation_returnsStatus() throws Exception {
+ Thread thread = new Thread(() -> {
+ int result = mTransportStatusCallback.getOperationStatus();
+ assertThat(result).isEqualTo(OPERATION_COMPLETE_STATUS);
+ });
+ thread.start();
+
+ mTransportStatusCallback.onOperationCompleteWithStatus(OPERATION_COMPLETE_STATUS);
+
+ thread.join();
+ }
+
+ @Test
+ public void testGetOperationStatus_operationTimesOut_returnsError() throws Exception {
+ TransportStatusCallback callback = new TransportStatusCallback(OPERATION_TIMEOUT_MILLIS);
+
+ int result = callback.getOperationStatus();
+
+ assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
index 2fe2f40f34be..b41a5311c89f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
@@ -16,6 +16,11 @@
package com.android.server.devicepolicy;
+import static android.os.UserHandle.USER_SYSTEM;
+
+import static com.android.server.devicepolicy.DevicePolicyManagerService.POLICIES_VERSION_XML;
+import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile;
+
import static com.google.common.truth.Truth.assertThat;
import android.app.admin.DeviceAdminInfo;
@@ -24,12 +29,15 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.os.Parcel;
+import android.os.UserHandle;
import android.util.TypedXmlPullParser;
import android.util.Xml;
import androidx.test.InstrumentationRegistry;
+import com.android.frameworks.servicestests.R;
import com.android.internal.util.JournaledFile;
+import com.android.server.SystemService;
import com.google.common.io.Files;
@@ -51,7 +59,7 @@ import java.util.Map;
import java.util.function.Function;
@RunWith(JUnit4.class)
-public class PolicyVersionUpgraderTest {
+public class PolicyVersionUpgraderTest extends DpmTestBase {
// NOTE: Only change this value if the corresponding CL also adds a test to test the upgrade
// to the new version.
private static final int LATEST_TESTED_VERSION = 2;
@@ -190,6 +198,40 @@ public class PolicyVersionUpgraderTest {
}
@Test
+ public void testNoStaleDataInCacheAfterUpgrade() throws Exception {
+ setUpPackageManagerForAdmin(admin1, UserHandle.getUid(USER_SYSTEM, 123 /* admin app ID */));
+ // Reusing COPE migration policy files there, only DO on user 0 is needed.
+ writeInputStreamToFile(getRawStream(R.raw.comp_policies_primary),
+ new File(getServices().systemUserDataDir, "device_policies.xml")
+ .getAbsoluteFile());
+ writeInputStreamToFile(getRawStream(R.raw.comp_device_owner),
+ new File(getServices().dataDir, "device_owner_2.xml")
+ .getAbsoluteFile());
+
+ // Write policy version 0
+ File versionFilePath =
+ new File(getServices().systemUserDataDir, POLICIES_VERSION_XML).getAbsoluteFile();
+ DpmTestUtils.writeToFile(versionFilePath, "0\n");
+
+ DevicePolicyManagerServiceTestable dpms;
+ final long ident = getContext().binder.clearCallingIdentity();
+ try {
+ dpms = new DevicePolicyManagerServiceTestable(getServices(), getContext());
+
+ // Simulate access that would cause policy data to be cached in mUserData.
+ dpms.isCommonCriteriaModeEnabled(null);
+
+ dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
+ } finally {
+ getContext().binder.restoreCallingIdentity(ident);
+ }
+
+ // DO should be marked as able to grant sensors permission during upgrade and should be
+ // reported as such via the API.
+ assertThat(dpms.canAdminGrantSensorsPermissionsForUser(/* userId= */0)).isTrue();
+ }
+
+ @Test
public void isLatestVersionTested() {
assertThat(DevicePolicyManagerService.DPMS_VERSION).isEqualTo(LATEST_TESTED_VERSION);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 847fe2ecf52d..7f7c716bc1f0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -61,6 +61,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Map;
@SmallTest
@Presubmit
@@ -130,6 +131,17 @@ public class ApexManagerTest {
}
@Test
+ public void testGetApexSystemServices() throws RemoteException {
+ when(mApexService.getAllPackages()).thenReturn(createApexInfoForTestPkg(true, false));
+ mApexManager.scanApexPackagesTraced(mPackageParser2,
+ ParallelPackageParser.makeExecutorService());
+
+ Map<String, String> services = mApexManager.getApexSystemServices();
+ assertThat(services).hasSize(1);
+ assertThat(services).containsKey("com.android.apex.test.ApexSystemService");
+ }
+
+ @Test
public void testGetActivePackages() throws RemoteException {
when(mApexService.getAllPackages()).thenReturn(createApexInfoForTestPkg(true, true));
mApexManager.scanApexPackagesTraced(mPackageParser2,
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 71d5b77306a4..28f24f2b55ee 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -470,9 +470,7 @@ public class ScanTests {
.addUsesPermission(
new ParsedUsesPermissionImpl(Manifest.permission.FACTORY_TEST, 0));
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(
- mMockPackageManager, mMockInjector);
- final ScanResult scanResult = scanPackageHelper.scanPackageOnlyLI(
+ final ScanResult scanResult = ScanPackageUtils.scanPackageOnlyLI(
createBasicScanRequestBuilder(basicPackage).build(),
mMockInjector,
true /*isUnderFactoryTest*/,
@@ -520,9 +518,7 @@ public class ScanTests {
private ScanResult executeScan(
ScanRequest scanRequest) throws PackageManagerException {
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(
- mMockPackageManager, mMockInjector);
- ScanResult result = scanPackageHelper.scanPackageOnlyLI(
+ ScanResult result = ScanPackageUtils.scanPackageOnlyLI(
scanRequest,
mMockInjector,
false /*isUnderFactoryTest*/,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 62a0dd4e7c17..419dda57c568 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -62,6 +62,7 @@ import static android.service.notification.NotificationListenerService.FLAG_FILT
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static com.google.common.truth.Truth.assertThat;
@@ -5482,6 +5483,39 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testRateLimitedToasts_windowsRemoved() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+ setToastRateIsWithinQuota(false); // rate limit reached
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
+ setAppInForegroundForToasts(mUid, false);
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ Binder token = new Binder();
+ INotificationManager nmService = (INotificationManager) mService.mService;
+
+ nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
+
+ // window token was added when enqueued
+ ArgumentCaptor<Binder> binderCaptor =
+ ArgumentCaptor.forClass(Binder.class);
+ verify(mWindowManagerInternal).addWindowToken(binderCaptor.capture(),
+ eq(TYPE_TOAST), anyInt(), eq(null));
+
+ // but never shown
+ verify(mStatusBar, times(0))
+ .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any());
+
+ // and removed when rate limited
+ verify(mWindowManagerInternal)
+ .removeWindowToken(eq(binderCaptor.getValue()), eq(true), anyInt());
+ }
+
+ @Test
public void backgroundSystemCustomToast_callsSetProcessImportantAsForegroundForToast() throws
Exception {
final String testPackage = "testPackageName";
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 e8a279907c5e..d49cf670f471 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -5112,6 +5112,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertTrue(expected.containsKey(uid));
assertThat(expected.get(uid)).isEqualTo(
builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
+
+ // pre-migration, the userSet field will always default to false
+ boolean userSet = builder.getBoolean(
+ PackageNotificationPreferences.USER_SET_IMPORTANCE_FIELD_NUMBER);
+ assertFalse(userSet);
}
}
}
@@ -5123,8 +5128,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// build a collection of app permissions that should be passed in but ignored
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
- appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, true)); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, true)); // in local prefs
// package preferences: PKG_O not banned based on local importance, and PKG_P is
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
@@ -5132,11 +5137,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// expected output. format: uid -> importance, as only uid (and not package name)
// is in PackageNotificationPreferences
- ArrayMap<Integer, Integer> expected = new ArrayMap<>();
- expected.put(1, IMPORTANCE_DEFAULT);
- expected.put(3, IMPORTANCE_NONE);
- expected.put(UID_O, IMPORTANCE_NONE); // banned by permissions
- expected.put(UID_P, IMPORTANCE_NONE); // defaults to none
+ ArrayMap<Integer, Pair<Integer, Boolean>> expected = new ArrayMap<>();
+ expected.put(1, new Pair(IMPORTANCE_DEFAULT, false));
+ expected.put(3, new Pair(IMPORTANCE_NONE, true));
+ expected.put(UID_O, new Pair(IMPORTANCE_NONE, true)); // banned by permissions
+ expected.put(UID_P, new Pair(IMPORTANCE_NONE, false)); // defaults to none, false
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackagePreferencesStats(events, appPermissions);
@@ -5144,11 +5149,14 @@ public class PreferencesHelperTest extends UiServiceTestCase {
for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
if (builder.getAtomId() == PACKAGE_NOTIFICATION_PREFERENCES) {
int uid = builder.getInt(PackageNotificationPreferences.UID_FIELD_NUMBER);
+ boolean userSet = builder.getBoolean(
+ PackageNotificationPreferences.USER_SET_IMPORTANCE_FIELD_NUMBER);
// if it's one of the expected ids, then make sure the importance matches
assertTrue(expected.containsKey(uid));
- assertThat(expected.get(uid)).isEqualTo(
+ assertThat(expected.get(uid).first).isEqualTo(
builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
+ assertThat(expected.get(uid).second).isEqualTo(userSet);
}
}
}
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 4a8e121dc3d1..2e62286f2969 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,6 +16,10 @@
package com.android.server.wm;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -103,6 +107,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import android.app.ActivityOptions;
+import android.app.ICompatCameraControlCallback;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.DestroyActivityItem;
@@ -3084,6 +3089,188 @@ public class ActivityRecordTests extends WindowTestsBase {
eq(null));
}
+ @Test
+ public void testUpdateCameraCompatState_flagIsEnabled_controlStateIsUpdated() {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being enabled.
+ doReturn(true).when(activity).isCameraCompatControlEnabled();
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+ }
+
+ @Test
+ public void testUpdateCameraCompatState_flagIsDisabled_controlStateIsHidden() {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being disabled.
+ doReturn(false).when(activity).isCameraCompatControlEnabled();
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+ }
+
+ @Test
+ public void testUpdateCameraCompatStateFromUser_clickedOnDismiss() throws RemoteException {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being enabled.
+ doReturn(true).when(activity).isCameraCompatControlEnabled();
+
+ ICompatCameraControlCallback callback = getCompatCameraControlCallback();
+ spyOn(callback);
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, callback);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Clicking on the button.
+ activity.updateCameraCompatStateFromUser(CAMERA_COMPAT_CONTROL_DISMISSED);
+
+ verify(callback, never()).revertCameraCompatTreatment();
+ verify(callback, never()).applyCameraCompatTreatment();
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
+
+ // All following updates are ignored.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
+
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
+ }
+
+ @Test
+ public void testUpdateCameraCompatStateFromUser_clickedOnApplyTreatment()
+ throws RemoteException {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being enabled.
+ doReturn(true).when(activity).isCameraCompatControlEnabled();
+
+ ICompatCameraControlCallback callback = getCompatCameraControlCallback();
+ spyOn(callback);
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, callback);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Clicking on the button.
+ activity.updateCameraCompatStateFromUser(CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ verify(callback, never()).revertCameraCompatTreatment();
+ verify(callback).applyCameraCompatTreatment();
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ // Request from the client to show the control are ignored respecting the user choice.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ // Request from the client to hide the control is respected.
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Request from the client to show the control again is respected.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ }
+
+ @Test
+ public void testUpdateCameraCompatStateFromUser_clickedOnRevertTreatment()
+ throws RemoteException {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being enabled.
+ doReturn(true).when(activity).isCameraCompatControlEnabled();
+
+ ICompatCameraControlCallback callback = getCompatCameraControlCallback();
+ spyOn(callback);
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, callback);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ // Clicking on the button.
+ activity.updateCameraCompatStateFromUser(CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(callback).revertCameraCompatTreatment();
+ verify(callback, never()).applyCameraCompatTreatment();
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Request from the client to show the control are ignored respecting the user choice.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Request from the client to hide the control is respected.
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Request from the client to show the control again is respected.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ }
+
+ private ICompatCameraControlCallback getCompatCameraControlCallback() {
+ return new ICompatCameraControlCallback.Stub() {
+ @Override
+ public void applyCameraCompatTreatment() {}
+
+ @Override
+ public void revertCameraCompatTreatment() {}
+ };
+ }
+
private void assertHasStartingWindow(ActivityRecord atoken) {
assertNotNull(atoken.mStartingSurface);
assertNotNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 3e617d58caa0..55d6df95e66f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -34,6 +34,7 @@ import static org.mockito.ArgumentMatchers.nullable;
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
@@ -58,6 +59,7 @@ import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.pm.PackageManagerService;
+import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult;
import org.junit.After;
import org.junit.Before;
@@ -298,35 +300,44 @@ public class ActivityStartInterceptorTest {
assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
}
- public void addMockInterceptorCallback(@Nullable Intent intent) {
+ public void addMockInterceptorCallback(
+ @Nullable Intent intent, @Nullable ActivityOptions activityOptions) {
int size = mActivityInterceptorCallbacks.size();
mActivityInterceptorCallbacks.put(size, new ActivityInterceptorCallback() {
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
- return intent;
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+ if (intent == null && activityOptions == null) {
+ return null;
+ }
+ return new ActivityInterceptResult(
+ intent != null ? intent : info.intent,
+ activityOptions != null ? activityOptions : info.checkedOptions);
}
});
}
@Test
public void testInterceptionCallback_singleCallback() {
- addMockInterceptorCallback(new Intent("android.test.foo"));
+ addMockInterceptorCallback(
+ new Intent("android.test.foo"),
+ ActivityOptions.makeBasic().setLaunchDisplayId(3));
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
assertEquals("android.test.foo", mInterceptor.mIntent.getAction());
+ assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId());
}
@Test
public void testInterceptionCallback_singleCallbackReturnsNull() {
- addMockInterceptorCallback(null);
+ addMockInterceptorCallback(null, null);
assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
}
@Test
public void testInterceptionCallback_fallbackToSecondCallback() {
- addMockInterceptorCallback(null);
- addMockInterceptorCallback(new Intent("android.test.second"));
+ addMockInterceptorCallback(null, null);
+ addMockInterceptorCallback(new Intent("android.test.second"), null);
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
assertEquals("android.test.second", mInterceptor.mIntent.getAction());
@@ -334,7 +345,7 @@ public class ActivityStartInterceptorTest {
@Test
public void testActivityLaunchedCallback_singleCallback() {
- addMockInterceptorCallback(null);
+ addMockInterceptorCallback(null, null);
assertEquals(1, mActivityInterceptorCallbacks.size());
final ActivityInterceptorCallback callback = mActivityInterceptorCallbacks.valueAt(0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index e0072b441577..a2b04c295944 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -53,7 +53,6 @@ import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
-import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
@@ -951,7 +950,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
@@ -963,7 +962,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
@@ -975,7 +974,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
@@ -983,7 +982,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
@@ -997,7 +996,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index deba83530b45..2ef59f6ac5c9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -681,7 +681,7 @@ public class DisplayContentTests extends WindowTestsBase {
final int maxWidth = 300;
final int resultingHeight = (maxWidth * baseHeight) / baseWidth;
- final int resultingDensity = baseDensity;
+ final int resultingDensity = (baseDensity * maxWidth) / baseWidth;
displayContent.setMaxUiWidth(maxWidth);
verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
@@ -756,6 +756,33 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
+ public void testSetForcedDensity() {
+ final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
+ final int baseWidth = 1280;
+ final int baseHeight = 720;
+ final int baseDensity = 320;
+
+ displayContent.mInitialDisplayWidth = baseWidth;
+ displayContent.mInitialDisplayHeight = baseHeight;
+ displayContent.mInitialDisplayDensity = baseDensity;
+ displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
+
+ final int forcedDensity = 600;
+
+ // Verify that forcing the density is honored and the size doesn't change.
+ displayContent.setForcedDensity(forcedDensity, 0 /* userId */);
+ verifySizes(displayContent, baseWidth, baseHeight, forcedDensity);
+
+ // Verify that forcing the density is idempotent.
+ displayContent.setForcedDensity(forcedDensity, 0 /* userId */);
+ verifySizes(displayContent, baseWidth, baseHeight, forcedDensity);
+
+ // Verify that forcing resolution won't affect the already forced density.
+ displayContent.setForcedSize(1800, 1200);
+ verifySizes(displayContent, 1800, 1200, forcedDensity);
+ }
+
+ @Test
public void testDisplayCutout_rot0() {
final DisplayContent dc = createNewDisplay();
dc.mInitialDisplayWidth = 200;
@@ -1395,18 +1422,16 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(config90.orientation, app.getConfiguration().orientation);
assertEquals(config90.windowConfiguration.getBounds(), app.getBounds());
- // Make wallaper laid out with the fixed rotation transform.
+ // Associate wallpaper with the fixed rotation transform.
final WindowToken wallpaperToken = mWallpaperWindow.mToken;
wallpaperToken.linkFixedRotationTransform(app);
- mWallpaperWindow.mLayoutNeeded = true;
- performLayout(mDisplayContent);
// Force the negative offset to verify it can be updated.
mWallpaperWindow.mXOffset = mWallpaperWindow.mYOffset = -1;
assertTrue(mDisplayContent.mWallpaperController.updateWallpaperOffset(mWallpaperWindow,
false /* sync */));
- assertThat(mWallpaperWindow.mXOffset).isGreaterThan(-1);
- assertThat(mWallpaperWindow.mYOffset).isGreaterThan(-1);
+ assertThat(mWallpaperWindow.mXOffset).isNotEqualTo(-1);
+ assertThat(mWallpaperWindow.mYOffset).isNotEqualTo(-1);
// The wallpaper need to animate with transformed position, so its surface position should
// not be reset.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index f5b361cff16d..97b1c91d156a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
@@ -56,7 +55,6 @@ public class DisplayPolicyTestsBase extends WindowTestsBase {
static final int DISPLAY_WIDTH = 500;
static final int DISPLAY_HEIGHT = 1000;
- static final int DISPLAY_DENSITY = 320;
static final int DISPLAY_CUTOUT_HEIGHT = 8;
static final int IME_HEIGHT = 415;
@@ -85,12 +83,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase {
doReturn(true).when(mDisplayPolicy).hasNavigationBar();
doReturn(true).when(mDisplayPolicy).hasStatusBar();
- final int shortSizeDp =
- Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY;
- final int longSizeDp =
- Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY;
- mDisplayContent.getDisplayRotation().configure(
- DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp, longSizeDp);
+ mDisplayContent.getDisplayRotation().configure(DISPLAY_WIDTH, DISPLAY_HEIGHT);
mDisplayPolicy.onConfigurationChanged();
addWindow(mStatusBarWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index e1aca55762d6..6342183ea28e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -681,7 +681,7 @@ public class DisplayRotationTests {
}
/**
- * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
+ * Call {@link DisplayRotation#configure(int, int)} to configure {@link #mTarget}
* according to given parameters.
*/
private void configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv) {
@@ -709,9 +709,7 @@ public class DisplayRotationTests {
when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
.thenReturn(isTv);
- final int shortSizeDp = (isCar || isTv) ? 540 : 720;
- final int longSizeDp = 960;
- mTarget.configure(width, height, shortSizeDp, longSizeDp);
+ mTarget.configure(width, height);
}
private void freezeRotation(int rotation) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index a7a374be7732..0a8b2e7ddbb8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -899,22 +899,21 @@ public class TaskTests extends WindowTestsBase {
/**
* Test that root activity index is reported correctly when looking for the 'effective root' in
- * case when bottom activity is finishing. Ignore the relinquishing task identity if it's not a
- * system activity even with the FLAG_RELINQUISH_TASK_IDENTITY.
+ * case when bottom activities are relinquishing task identity or finishing.
*/
@Test
public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() {
- final Task task = getTestTask();
+ final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = activity0.getTask();
// Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and
// one above as finishing.
- final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
activity1.finishing = true;
new ActivityBuilder(mAtm).setTask(task).build();
assertEquals("The first non-finishing activity and non-relinquishing task identity "
- + "must be reported.", task.getChildAt(0), task.getRootActivity(
+ + "must be reported.", task.getChildAt(2), task.getRootActivity(
false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
}
@@ -934,21 +933,21 @@ public class TaskTests extends WindowTestsBase {
}
/**
- * Test that the root activity index is reported correctly when looking for the
- * 'effective root' for the case when all non-system activities have relinquishTaskIdentity set.
+ * Test that the topmost activity index is reported correctly when looking for the
+ * 'effective root' for the case when all activities have relinquishTaskIdentity set.
*/
@Test
public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() {
- final Task task = getTestTask();
+ final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = activity0.getTask();
// Set relinquishTaskIdentity for all activities in the task
- final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
- assertEquals("The topmost activity in the task must be reported.", task.getChildAt(0),
- task.getRootActivity(false /*ignoreRelinquishIdentity*/,
- true /*setToBottomIfNone*/));
+ assertEquals("The topmost activity in the task must be reported.",
+ task.getChildAt(task.getChildCount() - 1), task.getRootActivity(
+ false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
}
/** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */
@@ -1086,14 +1085,14 @@ public class TaskTests extends WindowTestsBase {
}
/**
- * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with non-system
- * activity that relinquishes task identity.
+ * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that
+ * relinquishes task identity.
*/
@Test
public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() {
- final Task task = getTestTask();
+ final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = activity0.getTask();
// Make the current root activity relinquish task identity
- final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
// Add an extra activity on top - this will be the new root
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
@@ -1102,7 +1101,7 @@ public class TaskTests extends WindowTestsBase {
assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity0.token, true /* onlyRoot */));
- assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
+ assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity1.token, true /* onlyRoot */));
assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
ActivityRecord.getTaskForActivityLocked(activity2.token, true /* onlyRoot */));
@@ -1189,6 +1188,46 @@ public class TaskTests extends WindowTestsBase {
verify(task).setIntent(eq(activity0));
}
+ /**
+ * Test {@link Task#updateEffectiveIntent()} when activity with relinquishTaskIdentity but
+ * another with different uid. This should make the task use the root activity when updating the
+ * intent.
+ */
+ @Test
+ public void testUpdateEffectiveIntent_relinquishingWithDifferentUid() {
+ final ActivityRecord activity0 = new ActivityBuilder(mAtm)
+ .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build();
+ final Task task = activity0.getTask();
+
+ // Add an extra activity on top
+ new ActivityBuilder(mAtm).setUid(11).setTask(task).build();
+
+ spyOn(task);
+ task.updateEffectiveIntent();
+ verify(task).setIntent(eq(activity0));
+ }
+
+ /**
+ * Test {@link Task#updateEffectiveIntent()} with activities set as relinquishTaskIdentity.
+ * This should make the task use the topmost activity when updating the intent.
+ */
+ @Test
+ public void testUpdateEffectiveIntent_relinquishingMultipleActivities() {
+ final ActivityRecord activity0 = new ActivityBuilder(mAtm)
+ .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build();
+ final Task task = activity0.getTask();
+ // Add an extra activity on top
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+ activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+
+ // Add an extra activity on top
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
+
+ spyOn(task);
+ task.updateEffectiveIntent();
+ verify(task).setIntent(eq(activity2));
+ }
+
@Test
public void testSaveLaunchingStateWhenConfigurationChanged() {
LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index a97c0571257e..ec6cd9249317 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -525,6 +525,43 @@ public class TransitionTests extends WindowTestsBase {
}
@Test
+ public void testAppTransitionWithRotationChange() {
+ final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ makeWindowVisible(statusBar);
+ mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
+ final ActivityRecord app = createActivityRecord(mDisplayContent);
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+ final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
+ app.mTransitionController.requestStartTransition(transition, app.getTask(),
+ null /* remoteTransition */, null /* displayChange */);
+ mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
+ final int anyChanges = 1;
+ mDisplayContent.requestChangeTransitionIfNeeded(anyChanges, null /* displayChange */);
+ transition.setKnownConfigChanges(mDisplayContent, anyChanges);
+ final FadeRotationAnimationController fadeController =
+ mDisplayContent.getFadeRotationAnimationController();
+ assertNotNull(fadeController);
+ assertTrue(fadeController.shouldFreezeInsetsPosition(statusBar));
+
+ statusBar.setOrientationChanging(true);
+ player.startTransition();
+ // Non-app windows should not be collected.
+ assertFalse(statusBar.mToken.inTransition());
+ assertTrue(app.getTask().inTransition());
+
+ final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+ player.onTransactionReady(startTransaction);
+ // The leash should be unrotated.
+ verify(startTransaction).setMatrix(eq(statusBar.mToken.getAnimationLeash()), any(), any());
+
+ // The redrawn window will be faded in when the transition finishes. And because this test
+ // only use one non-activity window, the fade rotation controller should also be cleared.
+ statusBar.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+ player.finish();
+ assertNull(mDisplayContent.getFadeRotationAnimationController());
+ }
+
+ @Test
public void testIntermediateVisibility() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
final TransitionController controller = new TransitionController(mAtm, snapshotController);
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 2d3b9286d69a..88725a6ea1b9 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -552,23 +552,26 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
private static final int MSG_PACKAGE_REMOVED = 103;
/**
* By only triggering a re-calculation after the storage has changed sizes, we can avoid
- * recalculating quotas too often. Minimum change delta defines the percentage of change
- * we need to see before we recalculate.
+ * recalculating quotas too often. Minimum change delta high and low define the
+ * percentage of change we need to see before we recalculate quotas when the device has
+ * enough storage space (more than StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total
+ * free) and in low storage condition respectively.
*/
- private static final double MINIMUM_CHANGE_DELTA = 0.05;
+ private static final long MINIMUM_CHANGE_DELTA_PERCENT_HIGH = 5;
+ private static final long MINIMUM_CHANGE_DELTA_PERCENT_LOW = 2;
private static final int UNSET = -1;
private static final boolean DEBUG = false;
private final StatFs mStats;
private long mPreviousBytes;
- private double mMinimumThresholdBytes;
+ private long mTotalBytes;
public H(Looper looper) {
super(looper);
// TODO: Handle all private volumes.
mStats = new StatFs(Environment.getDataDirectory().getAbsolutePath());
mPreviousBytes = mStats.getAvailableBytes();
- mMinimumThresholdBytes = mStats.getTotalBytes() * MINIMUM_CHANGE_DELTA;
+ mTotalBytes = mStats.getTotalBytes();
}
public void handleMessage(Message msg) {
@@ -584,7 +587,14 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
case MSG_CHECK_STORAGE_DELTA: {
mStats.restat(Environment.getDataDirectory().getAbsolutePath());
long bytesDelta = Math.abs(mPreviousBytes - mStats.getAvailableBytes());
- if (bytesDelta > mMinimumThresholdBytes) {
+ long bytesDeltaThreshold;
+ if (mStats.getAvailableBytes() > mTotalBytes
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH / 100) {
+ bytesDeltaThreshold = mTotalBytes * MINIMUM_CHANGE_DELTA_PERCENT_HIGH / 100;
+ } else {
+ bytesDeltaThreshold = mTotalBytes * MINIMUM_CHANGE_DELTA_PERCENT_LOW / 100;
+ }
+ if (bytesDelta > bytesDeltaThreshold) {
mPreviousBytes = mStats.getAvailableBytes();
recalculateQuotas(getInitializedStrategy());
notifySignificantDelta();
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 4220fd78c46f..2fb67d79ccee 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -175,10 +175,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
// Delay for debouncing USB disconnects.
// We often get rapid connect/disconnect events when enabling USB functions,
// which need debouncing.
- private static final int DEVICE_STATE_UPDATE_DELAY = 3000;
-
- // Delay for debouncing USB disconnects on Type-C ports in host mode
- private static final int HOST_STATE_UPDATE_DELAY = 1000;
+ private static final int UPDATE_DELAY = 1000;
// Timeout for entering USB request mode.
// Request is cancelled if host does not configure device within 10 seconds.
@@ -648,7 +645,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
msg.arg1 = connected;
msg.arg2 = configured;
// debounce disconnects to avoid problems bringing up USB tethering
- sendMessageDelayed(msg, (connected == 0) ? DEVICE_STATE_UPDATE_DELAY : 0);
+ sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0);
}
public void updateHostState(UsbPort port, UsbPortStatus status) {
@@ -663,7 +660,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
removeMessages(MSG_UPDATE_PORT_STATE);
Message msg = obtainMessage(MSG_UPDATE_PORT_STATE, args);
// debounce rapid transitions of connect/disconnect on type-c ports
- sendMessageDelayed(msg, HOST_STATE_UPDATE_DELAY);
+ sendMessageDelayed(msg, UPDATE_DELAY);
}
private void setAdbEnabled(boolean enable) {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 98f619f497d4..0dc899e392a7 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
@@ -31,6 +32,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -70,6 +72,7 @@ import java.util.concurrent.Executor;
*/
@SuppressAutoDoc
@SystemService(Context.TELECOM_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELECOM)
public class TelecomManager {
/**
diff --git a/telephony/common/com/google/android/mms/util/SqliteWrapper.java b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
index e2d62f868d52..6d9b3210ea70 100644
--- a/telephony/common/com/google/android/mms/util/SqliteWrapper.java
+++ b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
@@ -17,7 +17,6 @@
package com.google.android.mms.util;
-import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -25,49 +24,15 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
-import android.os.Build;
import android.util.Log;
-import android.widget.Toast;
public final class SqliteWrapper {
private static final String TAG = "SqliteWrapper";
- private static final String SQLITE_EXCEPTION_DETAIL_MESSAGE
- = "unable to open database file";
private SqliteWrapper() {
// Forbidden being instantiated.
}
- // FIXME: It looks like outInfo.lowMemory does not work well as we expected.
- // after run command: adb shell fillup -p 100, outInfo.lowMemory is still false.
- private static boolean isLowMemory(Context context) {
- if (null == context) {
- return false;
- }
-
- ActivityManager am = (ActivityManager)
- context.getSystemService(Context.ACTIVITY_SERVICE);
- ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();
- am.getMemoryInfo(outInfo);
-
- return outInfo.lowMemory;
- }
-
- // FIXME: need to optimize this method.
- private static boolean isLowMemory(SQLiteException e) {
- return e.getMessage().equals(SQLITE_EXCEPTION_DETAIL_MESSAGE);
- }
-
- @UnsupportedAppUsage
- public static void checkSQLiteException(Context context, SQLiteException e) {
- if (isLowMemory(e)) {
- Toast.makeText(context, com.android.internal.R.string.low_memory,
- Toast.LENGTH_SHORT).show();
- } else {
- throw e;
- }
- }
-
@UnsupportedAppUsage
public static Cursor query(Context context, ContentResolver resolver, Uri uri,
String[] projection, String selection, String[] selectionArgs, String sortOrder) {
@@ -75,21 +40,10 @@ public final class SqliteWrapper {
return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
} catch (SQLiteException e) {
Log.e(TAG, "Catch a SQLiteException when query: ", e);
- checkSQLiteException(context, e);
return null;
}
}
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static boolean requery(Context context, Cursor cursor) {
- try {
- return cursor.requery();
- } catch (SQLiteException e) {
- Log.e(TAG, "Catch a SQLiteException when requery: ", e);
- checkSQLiteException(context, e);
- return false;
- }
- }
@UnsupportedAppUsage
public static int update(Context context, ContentResolver resolver, Uri uri,
ContentValues values, String where, String[] selectionArgs) {
@@ -97,7 +51,6 @@ public final class SqliteWrapper {
return resolver.update(uri, values, where, selectionArgs);
} catch (SQLiteException e) {
Log.e(TAG, "Catch a SQLiteException when update: ", e);
- checkSQLiteException(context, e);
return -1;
}
}
@@ -109,7 +62,6 @@ public final class SqliteWrapper {
return resolver.delete(uri, where, selectionArgs);
} catch (SQLiteException e) {
Log.e(TAG, "Catch a SQLiteException when delete: ", e);
- checkSQLiteException(context, e);
return -1;
}
}
@@ -121,7 +73,6 @@ public final class SqliteWrapper {
return resolver.insert(uri, values);
} catch (SQLiteException e) {
Log.e(TAG, "Catch a SQLiteException when insert: ", e);
- checkSQLiteException(context, e);
return null;
}
}
diff --git a/telephony/java/android/service/carrier/CarrierService.java b/telephony/java/android/service/carrier/CarrierService.java
index d06ec11f3e61..ae91d4d9b595 100644
--- a/telephony/java/android/service/carrier/CarrierService.java
+++ b/telephony/java/android/service/carrier/CarrierService.java
@@ -15,6 +15,8 @@
package android.service.carrier;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -22,9 +24,12 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyRegistryManager;
import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -87,7 +92,7 @@ public abstract class CarrierService extends Service {
* PersistableBundle} may be overridden by the system's default configuration service.
* </p>
*
- * @param id contains details about the current carrier that can be used do decide what
+ * @param id contains details about the current carrier that can be used to decide what
* configuration values to return. Instead of using details like MCCMNC to decide
* current carrier, it also contains subscription carrier id
* {@link android.telephony.TelephonyManager#getSimCarrierId()}, a platform-wide
@@ -95,10 +100,61 @@ public abstract class CarrierService extends Service {
* id as the key to look up the carrier info.
* @return a {@link PersistableBundle} object containing the configuration or null if default
* values should be used.
+ * @deprecated use {@link #onLoadConfig(int, CarrierIdentifier)} instead.
*/
+ @Deprecated
public abstract PersistableBundle onLoadConfig(CarrierIdentifier id);
/**
+ * Override this method to set carrier configuration on the given {@code subscriptionId}.
+ * <p>
+ * This method will be called by telephony services to get carrier-specific configuration
+ * values. The returned config will be saved by the system until,
+ * <ol>
+ * <li>The carrier app package is updated, or</li>
+ * <li>The carrier app requests a reload with
+ * {@link android.telephony.CarrierConfigManager#notifyConfigChangedForSubId
+ * notifyConfigChangedForSubId}.</li>
+ * </ol>
+ * This method can be called after a SIM card loads, which may be before or after boot.
+ * </p>
+ * <p>
+ * This method should not block for a long time. If expensive operations (e.g. network access)
+ * are required, this method can schedule the work and return null. Then, use
+ * {@link android.telephony.CarrierConfigManager#notifyConfigChangedForSubId
+ * notifyConfigChangedForSubId} to trigger a reload when the config is ready.
+ * </p>
+ * <p>
+ * Implementations should use the keys defined in {@link android.telephony.CarrierConfigManager
+ * CarrierConfigManager}. Any configuration values not set in the returned {@link
+ * PersistableBundle} may be overridden by the system's default configuration service.
+ * </p>
+ * <p>
+ * By default, this method just calls {@link #onLoadConfig(CarrierIdentifier)} with specified
+ * CarrierIdentifier {@code id}. Carrier app with target SDK
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU} and above should override this method to
+ * load carrier configuration on the given {@code subscriptionId}.
+ * Note that {@link #onLoadConfig(CarrierIdentifier)} is still called prior to
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ * </p>
+ *
+ * @param subscriptionId the subscription on which the carrier app should load configuration
+ * @param id contains details about the current carrier that can be used to decide what
+ * configuration values to return. Instead of using details like MCCMNC to decide
+ * current carrier, it also contains subscription carrier id
+ * {@link android.telephony.TelephonyManager#getSimCarrierId()}, a platform-wide
+ * unique identifier for each carrier, CarrierConfigService can directly use carrier
+ * id as the key to look up the carrier info.
+ * @return a {@link PersistableBundle} object containing the configuration or null if default
+ * values should be used.
+ */
+ @SuppressLint("NullableCollection")
+ @Nullable
+ public PersistableBundle onLoadConfig(int subscriptionId, @Nullable CarrierIdentifier id) {
+ return onLoadConfig(id);
+ }
+
+ /**
* Informs the system of an intentional upcoming carrier network change by
* a carrier app. This call is optional and is only used to allow the
* system to provide alternative UI while telephony is performing an action
@@ -115,7 +171,12 @@ public abstract class CarrierService extends Service {
* active. Set this value to true to begin showing
* alternative UI and false to stop.
* @see android.telephony.TelephonyManager#hasCarrierPrivileges
+ * @deprecated use {@link #notifyCarrierNetworkChange(int, boolean)} instead.
+ * With no parameter to specify the subscription, this API will
+ * apply to all subscriptions that the carrier app has carrier
+ * privileges on.
*/
+ @Deprecated
public final void notifyCarrierNetworkChange(boolean active) {
TelephonyRegistryManager telephonyRegistryMgr =
(TelephonyRegistryManager) this.getSystemService(
@@ -126,6 +187,31 @@ public abstract class CarrierService extends Service {
}
/**
+ * Informs the system of an intentional upcoming carrier network change by a carrier app on the
+ * given {@code subscriptionId}. This call is optional and is only used to allow the system to
+ * provide alternative UI while telephony is performing an action that may result in
+ * intentional, temporary network lack of connectivity.
+ *
+ * <p>Based on the active parameter passed in, this method will either show or hide the
+ * alternative UI. There is no timeout associated with showing this UX, so a carrier app must
+ * be sure to call with active set to false sometime after calling with it set to true.
+ *
+ * <p>Requires Permission: calling app has carrier privileges.
+ *
+ * @param subscriptionId the subscription of the carrier network that trigger the change.
+ * @param active whether the carrier network change is or shortly will be active. Set this
+ * value to true to begin showing alternative UI and false to stop.
+ * @see android.telephony.TelephonyManager#hasCarrierPrivileges
+ */
+ public final void notifyCarrierNetworkChange(int subscriptionId, boolean active) {
+ TelephonyRegistryManager telephonyRegistryMgr = this.getSystemService(
+ TelephonyRegistryManager.class);
+ if (telephonyRegistryMgr != null) {
+ telephonyRegistryMgr.notifyCarrierNetworkChange(subscriptionId, active);
+ }
+ }
+
+ /**
* If overriding this method, call through to the super method for any unknown actions.
* {@inheritDoc}
*/
@@ -149,10 +235,16 @@ public abstract class CarrierService extends Service {
public static final String KEY_CONFIG_BUNDLE = "config_bundle";
@Override
- public void getCarrierConfig(CarrierIdentifier id, ResultReceiver result) {
+ public void getCarrierConfig(int phoneId, CarrierIdentifier id, ResultReceiver result) {
try {
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ int[] subIds = SubscriptionManager.getSubId(phoneId);
+ if (!ArrayUtils.isEmpty(subIds)) {
+ // There should be at most one active subscription mapping to the phoneId.
+ subId = subIds[0];
+ }
Bundle data = new Bundle();
- data.putParcelable(KEY_CONFIG_BUNDLE, CarrierService.this.onLoadConfig(id));
+ data.putParcelable(KEY_CONFIG_BUNDLE, CarrierService.this.onLoadConfig(subId, id));
result.send(RESULT_OK, data);
} catch (Exception e) {
Log.e(LOG_TAG, "Error in onLoadConfig: " + e.getMessage(), e);
diff --git a/telephony/java/android/service/carrier/ICarrierService.aidl b/telephony/java/android/service/carrier/ICarrierService.aidl
index ac6f9614d8f5..054a280c3fe8 100644
--- a/telephony/java/android/service/carrier/ICarrierService.aidl
+++ b/telephony/java/android/service/carrier/ICarrierService.aidl
@@ -29,5 +29,5 @@ import android.service.carrier.CarrierIdentifier;
interface ICarrierService {
/** @see android.service.carrier.CarrierService#onLoadConfig */
- oneway void getCarrierConfig(in CarrierIdentifier id, in ResultReceiver result);
+ oneway void getCarrierConfig(in int phoneId, in CarrierIdentifier id, in ResultReceiver result);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c80d35b9e772..3f6d913a01a5 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
@@ -28,6 +29,7 @@ import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.SaProposal;
import android.os.Build;
@@ -48,12 +50,14 @@ import android.telephony.ims.feature.RcsFeature;
import com.android.internal.telephony.ICarrierConfigLoader;
import com.android.telephony.Rlog;
+import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Provides access to telephony configuration values that are carrier-specific.
*/
@SystemService(Context.CARRIER_CONFIG_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public class CarrierConfigManager {
private final static String TAG = "CarrierConfigManager";
@@ -556,9 +560,9 @@ public class CarrierConfigManager {
KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
/**
- * List of RIL radio technologies (See {@link ServiceState} {@code RIL_RADIO_TECHNOLOGY_*}
- * constants) which support only a single data connection at a time. Some carriers do not
- * support multiple pdp on UMTS.
+ * List of network type constants which support only a single data connection at a time.
+ * Some carriers do not support multiple PDP on UMTS.
+ * @see TelephonyManager NETWORK_TYPE_*
*/
public static final String
KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
@@ -3652,11 +3656,19 @@ public class CarrierConfigManager {
public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_ms_long";
/**
- * Which NR types are unmetered. A string array containing the following keys:
+ * Which network types are unmetered. A string array that can contain network type names from
+ * {@link TelephonyManager#getNetworkTypeName(int)} in addition to the following NR keys:
* NR_NSA - NR NSA is unmetered for sub-6 frequencies
* NR_NSA_MMWAVE - NR NSA is unmetered for mmwave frequencies
* NR_SA - NR SA is unmetered for sub-6 frequencies
* NR_SA_MMWAVE - NR SA is unmetered for mmwave frequencies
+ *
+ * Note that this config only applies if an unmetered SubscriptionPlan is set via
+ * {@link SubscriptionManager#setSubscriptionPlans(int, List)} or an unmetered override is set
+ * via {@link SubscriptionManager#setSubscriptionOverrideUnmetered(int, boolean, int[], long)}
+ * or {@link SubscriptionManager#setSubscriptionOverrideUnmetered(int, boolean, long)}.
+ * If neither SubscriptionPlans nor an override are set, then no network types can be unmetered
+ * regardless of the value of this config.
* TODO: remove other unmetered keys and replace with this
* @hide
*/
@@ -3664,6 +3676,27 @@ public class CarrierConfigManager {
"unmetered_network_types_string_array";
/**
+ * Which network types are unmetered when roaming. A string array that can contain network type
+ * names from {@link TelephonyManager#getNetworkTypeName(int)} in addition to the following
+ * NR keys:
+ * NR_NSA - NR NSA is unmetered when roaming for sub-6 frequencies
+ * NR_NSA_MMWAVE - NR NSA is unmetered when roaming for mmwave frequencies
+ * NR_SA - NR SA is unmetered when roaming for sub-6 frequencies
+ * NR_SA_MMWAVE - NR SA is unmetered when roaming for mmwave frequencies
+ *
+ * Note that this config only applies if an unmetered SubscriptionPlan is set via
+ * {@link SubscriptionManager#setSubscriptionPlans(int, List)} or an unmetered override is set
+ * via {@link SubscriptionManager#setSubscriptionOverrideUnmetered(int, boolean, int[], long)}
+ * or {@link SubscriptionManager#setSubscriptionOverrideUnmetered(int, boolean, long)}.
+ * If neither SubscriptionPlans nor an override are set, then no network types can be unmetered
+ * when roaming regardless of the value of this config.
+ * TODO: remove KEY_UNMETERED_NR_NSA_WHEN_ROAMING_BOOL and replace with this
+ * @hide
+ */
+ public static final String KEY_ROAMING_UNMETERED_NETWORK_TYPES_STRING_ARRAY =
+ "roaming_unmetered_network_types_string_array";
+
+ /**
* Whether NR (non-standalone) should be unmetered for all frequencies.
* If either {@link #KEY_UNMETERED_NR_NSA_MMWAVE_BOOL} or
* {@link #KEY_UNMETERED_NR_NSA_SUB6_BOOL} are true, then this value will be ignored.
@@ -3776,6 +3809,17 @@ public class CarrierConfigManager {
"esim_max_download_retry_attempts_int";
/**
+ * List of opportunistic carrier-ids associated with CBRS Primary SIM. When CBRS pSIM is
+ * inserted, opportunistic eSIM is download and this configuration is used for grouping pSIM
+ * and opportunistic eSIM. Also when a new CBRS pSIM is inserted, old opportunistic eSIMs are
+ * deleted using the carrier-ids in this configuration.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY =
+ "opportunistic_carrier_ids_int_array";
+
+ /**
* Controls RSRP threshold at which OpportunisticNetworkService will decide whether
* the opportunistic network is good enough for internet data.
*/
@@ -4455,6 +4499,29 @@ public class CarrierConfigManager {
"subscription_group_uuid_string";
/**
+ * Controls the cellular usage setting.
+ *
+ * The usage setting indicates whether a device will remain attached to a network based on
+ * the primary use case for the service. A device will detach and search for a more-preferred
+ * network if the primary use case (voice or data) is not satisfied. Depending on the type
+ * of device, it may operate in a voice or data-centric mode by default.
+ *
+ * <p>Sets the usage setting in accordance with 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
+ * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221
+ * Annex A.
+ *
+ * Either omit this key or pass a value of
+ * {@link SubscriptionManager#USAGE_SETTING_UNKNOWN unknown} to preserve the current setting.
+ *
+ * {@link SubscriptionManager#USAGE_SETTING_DEFAULT default},
+ * {@link SubscriptionManager#USAGE_SETTING_VOICE_CENTRIC voice-centric},
+ * or {@link SubscriptionManager#USAGE_SETTING_DATA_CENTRIC data-centric}.
+ * {@see SubscriptionInfo#getUsageSetting}
+ */
+ public static final String KEY_CELLULAR_USAGE_SETTING_INT =
+ "cellular_usage_setting_int";
+
+ /**
* Data switch validation minimal gap time, in milliseconds.
*
* Which means, if the same subscription on the same network (based on MCC+MNC+TAC+subId)
@@ -5570,14 +5637,9 @@ public class CarrierConfigManager {
sDefaults.putStringArray(KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
new String[]{""});
sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
- new int[]{
- 4, /* IS95A */
- 5, /* IS95B */
- 6, /* 1xRTT */
- 7, /* EVDO_0 */
- 8, /* EVDO_A */
- 12 /* EVDO_B */
- });
+ new int[] {TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_1xRTT,
+ TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_EVDO_A,
+ TelephonyManager.NETWORK_TYPE_EVDO_B});
sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putString(KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
@@ -5920,7 +5982,9 @@ public class CarrierConfigManager {
sDefaults.putInt(KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0);
sDefaults.putBoolean(KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL, true);
sDefaults.putBoolean(KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, false);
- sDefaults.putStringArray(KEY_UNMETERED_NETWORK_TYPES_STRING_ARRAY, new String[0]);
+ sDefaults.putStringArray(KEY_UNMETERED_NETWORK_TYPES_STRING_ARRAY, new String[] {
+ "NR_NSA", "NR_NSA_MMWAVE", "NR_SA", "NR_SA_MMWAVE"});
+ sDefaults.putStringArray(KEY_ROAMING_UNMETERED_NETWORK_TYPES_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_SUB6_BOOL, false);
@@ -5934,6 +5998,7 @@ public class CarrierConfigManager {
sDefaults.putString(KEY_SMDP_SERVER_ADDRESS_STRING, "");
sDefaults.putInt(KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT, 5);
sDefaults.putInt(KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT, 60);
+ sDefaults.putIntArray(KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY, new int[] {0});
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
@@ -6075,6 +6140,8 @@ public class CarrierConfigManager {
sDefaults.putStringArray(KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY, new String[]{
"source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, "
+ "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"});
+ sDefaults.putInt(KEY_CELLULAR_USAGE_SETTING_INT,
+ SubscriptionManager.USAGE_SETTING_UNKNOWN);
}
/**
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index fc76f99cf074..2758e1273cec 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -17,11 +17,13 @@
package android.telephony.ims;
import android.annotation.NonNull;
+import android.annotation.RequiresFeature;
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.telephony.BinderCacheManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
@@ -33,6 +35,7 @@ import com.android.internal.telephony.ITelephony;
* Provides access to information about Telephony IMS services on the device.
*/
@SystemService(Context.TELEPHONY_IMS_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public class ImsManager {
/**
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index 9cb80f1814f9..4884d549e2e0 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -19,6 +19,7 @@ package android.telephony;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
@@ -130,7 +131,10 @@ public final class SignalStrengthUpdateRequest implements Parcelable {
/**
* Set the builder object if require reporting on the system thresholds when device is idle.
*
- * <p>This can only used by the system caller. Requires permission
+ * <p>This is intended to be used by the system privileged caller only. When setting to
+ * {@code true}, signal strength update request through
+ * {@link TelephonyManager#setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}
+ * will require permission
* {@link android.Manifest.permission#LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH}.
*
* @param isSystemThresholdReportingRequestedWhileIdle true if request reporting on the
@@ -138,6 +142,7 @@ public final class SignalStrengthUpdateRequest implements Parcelable {
* @return the builder to facilitate the chaining
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
public @NonNull Builder setSystemThresholdReportingRequestedWhileIdle(
boolean isSystemThresholdReportingRequestedWhileIdle) {
@@ -191,6 +196,7 @@ public final class SignalStrengthUpdateRequest implements Parcelable {
*
* @hide
*/
+ @SystemApi
public boolean isSystemThresholdReportingRequestedWhileIdle() {
return mIsSystemThresholdReportingRequestedWhileIdle;
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 4339cd20416f..54fb65cea617 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -22,6 +22,7 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SuppressAutoDoc;
import android.annotation.SystemApi;
@@ -32,6 +33,7 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.database.CursorWindow;
import android.net.Uri;
import android.os.Build;
@@ -75,6 +77,7 @@ import java.util.concurrent.Executor;
*
* @see SubscriptionManager#getActiveSubscriptionInfoList()
*/
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
public final class SmsManager {
private static final String TAG = "SmsManager";
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index d11ad914061d..c36eb2f9cf65 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -37,6 +37,7 @@ import android.os.Build;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.Parcelable;
+import android.telephony.SubscriptionManager.UsageSetting;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -227,6 +228,11 @@ public class SubscriptionInfo implements Parcelable {
private final int mPortIndex;
/**
+ * Subscription's preferred usage setting.
+ */
+ private @UsageSetting int mUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN;
+
+ /**
* Public copy constructor.
* @hide
*/
@@ -284,6 +290,7 @@ public class SubscriptionInfo implements Parcelable {
cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierId, profileClass,
subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, 0);
}
+
/**
* @hide
*/
@@ -295,6 +302,24 @@ public class SubscriptionInfo implements Parcelable {
int carrierId, int profileClass, int subType, @Nullable String groupOwner,
@Nullable UiccAccessRule[] carrierConfigAccessRules,
boolean areUiccApplicationsEnabled, int portIndex) {
+ this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString,
+ cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierId, profileClass,
+ subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled,
+ portIndex, SubscriptionManager.USAGE_SETTING_DEFAULT);
+ }
+
+ /**
+ * @hide
+ */
+ public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+ CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+ Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
+ @Nullable UiccAccessRule[] nativeAccessRules, String cardString, int cardId,
+ boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled,
+ int carrierId, int profileClass, int subType, @Nullable String groupOwner,
+ @Nullable UiccAccessRule[] carrierConfigAccessRules,
+ boolean areUiccApplicationsEnabled, int portIndex, @UsageSetting int usageSetting) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -322,6 +347,7 @@ public class SubscriptionInfo implements Parcelable {
this.mCarrierConfigAccessRules = carrierConfigAccessRules;
this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
this.mPortIndex = portIndex;
+ this.mUsageSetting = usageSetting;
}
/**
* @return the subscription ID.
@@ -796,7 +822,18 @@ public class SubscriptionInfo implements Parcelable {
return mAreUiccApplicationsEnabled;
}
- public static final @android.annotation.NonNull Parcelable.Creator<SubscriptionInfo> CREATOR = new Parcelable.Creator<SubscriptionInfo>() {
+ /**
+ * Get the usage setting for this subscription.
+ *
+ * @return the usage setting used for this subscription.
+ */
+ public @UsageSetting int getUsageSetting() {
+ return mUsageSetting;
+ }
+
+ public static final @android.annotation.NonNull
+ Parcelable.Creator<SubscriptionInfo> CREATOR =
+ new Parcelable.Creator<SubscriptionInfo>() {
@Override
public SubscriptionInfo createFromParcel(Parcel source) {
int id = source.readInt();
@@ -828,12 +865,14 @@ public class SubscriptionInfo implements Parcelable {
UiccAccessRule[] carrierConfigAccessRules = source.createTypedArray(
UiccAccessRule.CREATOR);
boolean areUiccApplicationsEnabled = source.readBoolean();
+ int usageSetting = source.readInt();
SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
carrierName, nameSource, iconTint, number, dataRoaming, /* icon= */ null,
mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, cardId,
isOpportunistic, groupUUID, isGroupDisabled, carrierid, profileClass, subType,
- groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, portId);
+ groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled,
+ portId, usageSetting);
info.setAssociatedPlmns(ehplmns, hplmns);
return info;
}
@@ -875,6 +914,7 @@ public class SubscriptionInfo implements Parcelable {
dest.writeString(mGroupOwner);
dest.writeTypedArray(mCarrierConfigAccessRules, flags);
dest.writeBoolean(mAreUiccApplicationsEnabled);
+ dest.writeInt(mUsageSetting);
}
@Override
@@ -919,7 +959,8 @@ public class SubscriptionInfo implements Parcelable {
+ " subscriptionType=" + mSubscriptionType
+ " groupOwner=" + mGroupOwner
+ " carrierConfigAccessRules=" + Arrays.toString(mCarrierConfigAccessRules)
- + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled + "}";
+ + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
+ + " usageSetting=" + mUsageSetting + "}";
}
@Override
@@ -927,7 +968,8 @@ public class SubscriptionInfo implements Parcelable {
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso, mCardString,
mCardId, mDisplayName, mCarrierName, mNativeAccessRules, mIsGroupDisabled,
- mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex);
+ mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex,
+ mUsageSetting);
}
@Override
@@ -967,6 +1009,7 @@ public class SubscriptionInfo implements Parcelable {
&& Arrays.equals(mNativeAccessRules, toCompare.mNativeAccessRules)
&& mProfileClass == toCompare.mProfileClass
&& Arrays.equals(mEhplmns, toCompare.mEhplmns)
- && Arrays.equals(mHplmns, toCompare.mHplmns);
+ && Arrays.equals(mHplmns, toCompare.mHplmns)
+ && mUsageSetting == toCompare.mUsageSetting;
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index f72832438204..2295ed7f3876 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -26,6 +26,7 @@ import android.annotation.DurationMillisLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -92,6 +93,7 @@ import java.util.stream.Collectors;
* and provides information about the current Telephony Subscriptions.
*/
@SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public class SubscriptionManager {
private static final String LOG_TAG = "SubscriptionManager";
private static final boolean DBG = false;
@@ -941,6 +943,13 @@ public class SubscriptionManager {
public static final String PROFILE_CLASS = SimInfo.COLUMN_PROFILE_CLASS;
/**
+ * TelephonyProvider column name for the port index of the active UICC port.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String PORT_INDEX = SimInfo.COLUMN_PORT_INDEX;
+
+ /**
* TelephonyProvider column name for VoIMS opt-in status.
*
* <P>Type: INTEGER (int)</P>
@@ -1030,12 +1039,75 @@ public class SubscriptionManager {
public static final String UICC_APPLICATIONS_ENABLED = SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED;
/**
- * Indicate which network type is allowed. By default it's enabled.
+ * Indicate which network type is allowed.
* @hide
*/
public static final String ALLOWED_NETWORK_TYPES =
SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"USAGE_SETTING_"},
+ value = {
+ USAGE_SETTING_UNKNOWN,
+ USAGE_SETTING_DEFAULT,
+ USAGE_SETTING_VOICE_CENTRIC,
+ USAGE_SETTING_DATA_CENTRIC})
+ public @interface UsageSetting {}
+
+ /**
+ * The usage setting is unknown.
+ *
+ * This will be the usage setting returned on devices that do not support querying the
+ * or setting the usage setting.
+ *
+ * It may also be provided by a carrier that wishes to provide a value to avoid making any
+ * settings changes.
+ */
+ public static final int USAGE_SETTING_UNKNOWN = -1;
+
+ /**
+ * Subscription uses the default setting.
+ *
+ * The value is based upon device capability and the other properties of the subscription.
+ *
+ * Most subscriptions will default to voice-centric when in a phone.
+ *
+ * An opportunistic subscription will default to data-centric.
+ *
+ * {@see SubscriptionInfo#isOpportunistic}
+ */
+ public static final int USAGE_SETTING_DEFAULT = 0;
+
+ /**
+ * This subscription is forced to voice-centric mode
+ *
+ * <p>Refer to voice-centric mode in 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
+ * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221
+ * Annex A.
+ */
+ public static final int USAGE_SETTING_VOICE_CENTRIC = 1;
+
+ /**
+ * This subscription is forced to data-centric mode
+ *
+ * <p>Refer to data-centric mode in 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
+ * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221
+ * Annex A.
+ */
+ public static final int USAGE_SETTING_DATA_CENTRIC = 2;
+
+ /**
+ * Indicate the preferred usage setting for the subscription.
+ *
+ * 0 - Default - If the value has not been explicitly set, it will be "default"
+ * 1 - Voice-centric
+ * 2 - Data-centric
+ *
+ * @hide
+ */
+ public static final String USAGE_SETTING = SimInfo.COLUMN_USAGE_SETTING;
+
/**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
@@ -3943,4 +4015,33 @@ public class SubscriptionManager {
throw ex.rethrowAsRuntimeException();
}
}
+
+ /**
+ * Set the preferred usage setting.
+ *
+ * The cellular usage setting is a switch which controls the mode of operation for the cellular
+ * radio to either require or not require voice service. It is not managed via Android’s
+ * Settings.
+ *
+ * @param subscriptionId the subId of the subscription.
+ * @param usageSetting the requested usage setting.
+ *
+ * @throws IllegalStateException if a specific mode or setting the mode is not supported on a
+ * particular device.
+ *
+ * <p>Requires {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ * or that the calling app has CarrierPrivileges for the given subscription.
+ *
+ * Note: This method will not allow the setting of USAGE_SETTING_UNKNOWN.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ void setUsageSetting(int subscriptionId, @UsageSetting int usageSetting) {
+ if (VDBG) logd("[setUsageSetting]+ setting:" + usageSetting + " subId:" + subscriptionId);
+ setSubscriptionPropertyHelper(subscriptionId, "setUsageSetting",
+ (iSub)-> iSub.setUsageSetting(
+ usageSetting, subscriptionId, mContext.getOpPackageName()));
+ }
}
+
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index cca5e2f164dc..2a600d5afb88 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -49,6 +49,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.Uri;
@@ -70,6 +71,7 @@ import android.os.SystemProperties;
import android.os.WorkSource;
import android.provider.Settings.SettingNotFoundException;
import android.service.carrier.CarrierIdentifier;
+import android.service.carrier.CarrierService;
import android.sysprop.TelephonyProperties;
import android.telecom.CallScreeningService;
import android.telecom.InCallService;
@@ -172,6 +174,7 @@ import java.util.stream.IntStream;
* that do not implement this feature, the behavior is not reliable.
*/
@SystemService(Context.TELEPHONY_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public class TelephonyManager {
private static final String TAG = "TelephonyManager";
@@ -320,11 +323,19 @@ public class TelephonyManager {
public static final int UNINITIALIZED_CARD_ID = -2;
/**
- * Default port index for the UICC Card
- * @hide
+ * Default port index for a UICC.
+ *
+ * On physical SIM cards the only available port is 0.
+ * See {@link android.telephony.UiccPortInfo} for more information on ports.
+ *
+ * See {@link android.telephony.euicc.EuiccManager#isSimPortAvailable(int)} for information on
+ * how portIndex is used on eUICCs.
*/
public static final int DEFAULT_PORT_INDEX = 0;
+ /** @hide */
+ public static final int INVALID_PORT_INDEX = -1;
+
private final Context mContext;
private final int mSubId;
@UnsupportedAppUsage
@@ -2050,6 +2061,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
public String getImei() {
return getImei(getSlotIndex());
}
@@ -2091,6 +2103,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
public String getImei(int slotIndex) {
ITelephony telephony = getITelephony();
if (telephony == null) return null;
@@ -2108,6 +2121,7 @@ public class TelephonyManager {
* Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
* available.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
@Nullable
public String getTypeAllocationCode() {
return getTypeAllocationCode(getSlotIndex());
@@ -2119,6 +2133,7 @@ public class TelephonyManager {
*
* @param slotIndex of which Type Allocation Code is returned
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
@Nullable
public String getTypeAllocationCode(int slotIndex) {
ITelephony telephony = getITelephony();
@@ -2165,6 +2180,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getMeid() {
return getMeid(getSlotIndex());
}
@@ -2203,6 +2219,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getMeid(int slotIndex) {
ITelephony telephony = getITelephony();
if (telephony == null) return null;
@@ -2226,6 +2243,7 @@ public class TelephonyManager {
* Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
* available.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@Nullable
public String getManufacturerCode() {
return getManufacturerCode(getSlotIndex());
@@ -2237,6 +2255,7 @@ public class TelephonyManager {
*
* @param slotIndex of which Type Allocation Code is returned
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@Nullable
public String getManufacturerCode(int slotIndex) {
ITelephony telephony = getITelephony();
@@ -2282,6 +2301,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String getNai() {
return getNaiBySubscriberId(getSubId());
}
@@ -2615,6 +2635,7 @@ public class TelephonyManager {
* unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
* on a CDMA network).
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public String getNetworkOperatorName() {
return getNetworkOperatorName(getSubId());
}
@@ -2642,6 +2663,7 @@ public class TelephonyManager {
* unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
* on a CDMA network).
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public String getNetworkOperator() {
return getNetworkOperatorForPhone(getPhoneId());
}
@@ -2690,6 +2712,7 @@ public class TelephonyManager {
* @see #createForSubscriptionId(int)
* @see #createForPhoneAccountHandle(PhoneAccountHandle)
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public String getNetworkSpecifier() {
return String.valueOf(getSubId());
}
@@ -2712,6 +2735,7 @@ public class TelephonyManager {
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@WorkerThread
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public PersistableBundle getCarrierConfig() {
CarrierConfigManager carrierConfigManager = mContext
.getSystemService(CarrierConfigManager.class);
@@ -2724,6 +2748,7 @@ public class TelephonyManager {
* <p>
* Availability: Only when user registered to a network.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean isNetworkRoaming() {
return isNetworkRoaming(getSubId());
}
@@ -2753,6 +2778,7 @@ public class TelephonyManager {
* @return the lowercase 2 character ISO-3166-1 alpha-2 country code, or empty string if not
* available.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public String getNetworkCountryIso() {
return getNetworkCountryIso(getSlotIndex());
}
@@ -2775,6 +2801,7 @@ public class TelephonyManager {
* @throws IllegalArgumentException when the slotIndex is invalid.
*
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@NonNull
public String getNetworkCountryIso(int slotIndex) {
try {
@@ -2992,6 +3019,7 @@ public class TelephonyManager {
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PHONE_STATE,
android.Manifest.permission.READ_BASIC_PHONE_STATE})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public @NetworkType int getDataNetworkType() {
return getDataNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
}
@@ -3037,6 +3065,7 @@ public class TelephonyManager {
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PHONE_STATE,
android.Manifest.permission.READ_BASIC_PHONE_STATE})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public @NetworkType int getVoiceNetworkType() {
return getVoiceNetworkType(getSubId());
}
@@ -3374,6 +3403,7 @@ public class TelephonyManager {
/**
* @return true if a ICC card is present
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean hasIccCard() {
return hasIccCard(getSlotIndex());
}
@@ -3416,6 +3446,7 @@ public class TelephonyManager {
* @see #SIM_STATE_CARD_IO_ERROR
* @see #SIM_STATE_CARD_RESTRICTED
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @SimState int getSimState() {
int simState = getSimStateIncludingLoaded();
if (simState == SIM_STATE_LOADED) {
@@ -3458,6 +3489,7 @@ public class TelephonyManager {
* @hide
*/
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @SimState int getSimCardState() {
int simState = getSimState();
return getSimCardStateFromSimState(simState);
@@ -3503,6 +3535,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @SimState int getSimCardState(int physicalSlotIndex, int portIndex) {
int simState = getSimState(getLogicalSlotIndex(physicalSlotIndex, portIndex));
return getSimCardStateFromSimState(simState);
@@ -3559,6 +3592,7 @@ public class TelephonyManager {
* @hide
*/
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @SimState int getSimApplicationState() {
int simState = getSimStateIncludingLoaded();
return getSimApplicationStateFromSimState(simState);
@@ -3611,6 +3645,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @SimState int getSimApplicationState(int physicalSlotIndex, int portIndex) {
int simState =
SubscriptionManager.getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex,
@@ -3652,6 +3687,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean isApplicationOnUicc(@UiccAppType int appType) {
try {
ITelephony service = getITelephony();
@@ -3680,6 +3716,7 @@ public class TelephonyManager {
* @see #SIM_STATE_CARD_IO_ERROR
* @see #SIM_STATE_CARD_RESTRICTED
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @SimState int getSimState(int slotIndex) {
int simState = SubscriptionManager.getSimStateForSlotIndex(slotIndex);
if (simState == SIM_STATE_LOADED) {
@@ -3696,6 +3733,7 @@ public class TelephonyManager {
*
* @see #getSimState
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String getSimOperator() {
return getSimOperatorNumeric();
}
@@ -3780,6 +3818,7 @@ public class TelephonyManager {
*
* @see #getSimState
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String getSimOperatorName() {
return getSimOperatorNameForPhone(getPhoneId());
}
@@ -3817,6 +3856,7 @@ public class TelephonyManager {
* @return the lowercase 2 character ISO-3166-1 alpha-2 country code, or empty string is not
* available.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String getSimCountryIso() {
return getSimCountryIsoForPhone(getPhoneId());
}
@@ -3875,6 +3915,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String getSimSerialNumber() {
return getSimSerialNumber(getSubId());
}
@@ -3942,6 +3983,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean isLteCdmaEvdoGsmWcdmaEnabled() {
return getLteOnCdmaMode(getSubId()) == PhoneConstants.LTE_ON_CDMA_TRUE;
}
@@ -3985,6 +4027,7 @@ public class TelephonyManager {
*
* @return card ID of the default eUICC card, if loaded.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
public int getCardIdForDefaultEuicc() {
try {
ITelephony telephony = getITelephony();
@@ -4018,6 +4061,7 @@ public class TelephonyManager {
* the caller does not have adequate permissions for that card.
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@NonNull
public List<UiccCardInfo> getUiccCardsInfo() {
try {
@@ -4043,6 +4087,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public UiccSlotInfo[] getUiccSlotsInfo() {
try {
ITelephony telephony = getITelephony();
@@ -4185,6 +4230,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public void setSimSlotMapping(@NonNull Collection<UiccSlotMapping> slotMapping) {
try {
ITelephony telephony = getITelephony();
@@ -4248,6 +4294,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@NonNull
public Collection<UiccSlotMapping> getSimSlotMapping() {
List<UiccSlotMapping> slotMap;
@@ -4303,6 +4350,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String getSubscriberId() {
return getSubscriberId(getSubId());
}
@@ -4354,6 +4402,7 @@ public class TelephonyManager {
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@SystemApi
@Nullable
public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(@KeyType int keyType) {
@@ -4398,6 +4447,7 @@ public class TelephonyManager {
* @hide
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@SystemApi
public void resetCarrierKeysForImsiEncryption() {
try {
@@ -4597,6 +4647,7 @@ public class TelephonyManager {
* @param callback A callback called when the upload operation terminates, either in success
* or in error.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void uploadCallComposerPicture(@NonNull Path pictureToUpload,
@NonNull String contentType,
@CallbackExecutor @NonNull Executor executor,
@@ -4703,6 +4754,7 @@ public class TelephonyManager {
* @param callback A callback called when the upload operation terminates, either in success
* or in error.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void uploadCallComposerPicture(@NonNull InputStream pictureToUpload,
@NonNull String contentType,
@CallbackExecutor @NonNull Executor executor,
@@ -4838,6 +4890,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String getGroupIdLevel1() {
try {
IPhoneSubInfo info = getSubscriberInfoService();
@@ -5093,6 +5146,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @NonNull String[] getMergedImsisFromGroup() {
try {
ITelephony telephony = getITelephony();
@@ -5172,6 +5226,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public String getVoiceMailNumber() {
return getVoiceMailNumber(getSubId());
}
@@ -5207,6 +5262,7 @@ public class TelephonyManager {
* @param alphaTag The alpha tag to display.
* @param number The voicemail number.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean setVoiceMailNumber(String alphaTag, String number) {
return setVoiceMailNumber(getSubId(), alphaTag, number);
}
@@ -5246,6 +5302,7 @@ public class TelephonyManager {
* be implemented instead.
*/
@SystemApi
+ @Deprecated
@SuppressLint("RequiresPermission")
public void setVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle, boolean enabled){
}
@@ -5260,6 +5317,7 @@ public class TelephonyManager {
* be implemented instead.
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@SuppressLint("RequiresPermission")
public boolean isVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle){
@@ -5281,6 +5339,7 @@ public class TelephonyManager {
*/
@SystemApi
@SuppressLint("RequiresPermission")
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@Nullable
public Bundle getVisualVoicemailSettings(){
try {
@@ -5310,6 +5369,7 @@ public class TelephonyManager {
@Nullable
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public String getVisualVoicemailPackageName() {
try {
ITelephony telephony = getITelephony();
@@ -5336,6 +5396,7 @@ public class TelephonyManager {
* @see TelecomManager#getDefaultDialerPackage()
* @see CarrierConfigManager#KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings settings) {
if (settings == null) {
disableVisualVoicemailSmsFilter(mSubId);
@@ -5365,6 +5426,7 @@ public class TelephonyManager {
* @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
* @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void sendVisualVoicemailSms(String number, int port, String text,
PendingIntent sentIntent) {
sendVisualVoicemailSmsForSubscriber(mSubId, number, port, text, sentIntent);
@@ -5552,6 +5614,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void setVoiceActivationState(@SimActivationState int activationState) {
setVoiceActivationState(getSubId(), activationState);
}
@@ -5599,6 +5662,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public void setDataActivationState(@SimActivationState int activationState) {
setDataActivationState(getSubId(), activationState);
}
@@ -5646,6 +5710,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public @SimActivationState int getVoiceActivationState() {
return getVoiceActivationState(getSubId());
}
@@ -5695,6 +5760,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public @SimActivationState int getDataActivationState() {
return getDataActivationState(getSubId());
}
@@ -5771,6 +5837,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public String getVoiceMailAlphaTag() {
return getVoiceMailAlphaTag(getSubId());
}
@@ -5853,6 +5920,7 @@ public class TelephonyManager {
@Nullable
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String getIsimDomain() {
try {
IPhoneSubInfo info = getSubscriberInfoService();
@@ -5953,6 +6021,7 @@ public class TelephonyManager {
* @return The call state of the subscription associated with this TelephonyManager instance.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public @CallState int getCallStateForSubscription() {
return getCallState(getSubId());
}
@@ -6051,6 +6120,7 @@ public class TelephonyManager {
* @see #DATA_ACTIVITY_INOUT
* @see #DATA_ACTIVITY_DORMANT
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public int getDataActivity() {
try {
ITelephony telephony = getITelephony();
@@ -6075,6 +6145,7 @@ public class TelephonyManager {
DATA_CONNECTED,
DATA_SUSPENDED,
DATA_DISCONNECTING,
+ DATA_HANDOVER_IN_PROGRESS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DataState{}
@@ -6099,6 +6170,12 @@ public class TelephonyManager {
public static final int DATA_DISCONNECTING = 4;
/**
+ * Data connection state: Handover in progress. The connection is being transited from cellular
+ * network to IWLAN, or from IWLAN to cellular network.
+ */
+ public static final int DATA_HANDOVER_IN_PROGRESS = 5;
+
+ /**
* Used for checking if the SDK version for {@link TelephonyManager#getDataState} is above Q.
*/
@ChangeId
@@ -6114,7 +6191,9 @@ public class TelephonyManager {
* @see #DATA_CONNECTED
* @see #DATA_SUSPENDED
* @see #DATA_DISCONNECTING
+ * @see #DATA_HANDOVER_IN_PROGRESS
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public int getDataState() {
try {
ITelephony telephony = getITelephony();
@@ -6391,6 +6470,7 @@ public class TelephonyManager {
* on any device with a telephony radio, even if the device is
* data-only.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean isVoiceCapable() {
if (mContext == null) return true;
return mContext.getResources().getBoolean(
@@ -6406,6 +6486,7 @@ public class TelephonyManager {
* Note: Voicemail waiting sms, cell broadcasting sms, and MMS are
* disabled when device doesn't support sms.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
public boolean isSmsCapable() {
if (mContext == null) return true;
return mContext.getResources().getBoolean(
@@ -6455,6 +6536,7 @@ public class TelephonyManager {
* information is unavailable.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public List<CellInfo> getAllCellInfo() {
try {
ITelephony telephony = getITelephony();
@@ -6549,6 +6631,7 @@ public class TelephonyManager {
* @param callback a callback to receive CellInfo.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void requestCellInfoUpdate(
@NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
try {
@@ -6612,6 +6695,7 @@ public class TelephonyManager {
@SystemApi
@RequiresPermission(allOf = {android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.MODIFY_PHONE_STATE})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void requestCellInfoUpdate(@NonNull WorkSource workSource,
@NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
try {
@@ -6693,6 +6777,7 @@ public class TelephonyManager {
/**
* Returns the MMS user agent.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
public String getMmsUserAgent() {
try {
ITelephony telephony = getITelephony();
@@ -6708,6 +6793,7 @@ public class TelephonyManager {
/**
* Returns the MMS user agent profile URL.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
public String getMmsUAProfUrl() {
try {
ITelephony telephony = getITelephony();
@@ -6766,10 +6852,13 @@ public class TelephonyManager {
* @param p2 P2 parameter (described in ISO 7816-4).
* @return an IccOpenLogicalChannelResponse object.
* @hide
+ * @deprecated instead use {@link #iccOpenLogicalChannelByPort(int, int, String, int)}
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@SystemApi
@Nullable
+ @Deprecated
public IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int slotIndex,
@Nullable String aid, int p2) {
try {
@@ -6790,6 +6879,58 @@ public class TelephonyManager {
}
/**
+ * Opens a logical channel to the ICC card using the physical slot index and port index.
+ *
+ * Use this method when no subscriptions are available on the SIM and the operation must be
+ * performed using the physical slot index and port index.
+ *
+ * This operation wraps two APDU instructions:
+ * <ul>
+ * <li>MANAGE CHANNEL to open a logical channel</li>
+ * <li>SELECT the given {@code AID} using the given {@code p2}</li>
+ * </ul>
+ *
+ * Per Open Mobile API Specification v3.2 section 6.2.7.h, only p2 values of 0x00, 0x04, 0x08,
+ * and 0x0C are guaranteed to be supported.
+ *
+ * If the SELECT command's status word is not '9000', '62xx', or '63xx', the status word will be
+ * considered an error and the channel shall not be opened.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CCHO command.
+ *
+ * @param slotIndex the physical slot index of the ICC card
+ * @param portIndex The port index is an enumeration of the ports available on the UICC.
+ * Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+ * @param aid Application id. See ETSI 102.221 and 101.220.
+ * @param p2 P2 parameter (described in ISO 7816-4).
+ * @return an IccOpenLogicalChannelResponse object.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ @NonNull
+ public IccOpenLogicalChannelResponse iccOpenLogicalChannelByPort(int slotIndex,
+ int portIndex, @Nullable String aid, int p2) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ IccLogicalChannelRequest request = new IccLogicalChannelRequest();
+ request.slotIndex = slotIndex;
+ request.portIndex = portIndex;
+ request.aid = aid;
+ request.p2 = p2;
+ request.callingPackage = getOpPackageName();
+ request.binder = new Binder();
+ return telephony.iccOpenLogicalChannel(request);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Opens a logical channel to the ICC card.
*
* This operation wraps two APDU instructions:
@@ -6814,6 +6955,7 @@ public class TelephonyManager {
* @param p2 P2 parameter (described in ISO 7816-4).
* @return an IccOpenLogicalChannelResponse object.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) {
return iccOpenLogicalChannel(getSubId(), AID, p2);
}
@@ -6879,9 +7021,12 @@ public class TelephonyManager {
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
* @hide
+ * @deprecated instead use {@link #iccCloseLogicalChannelByPort(int, int, int)}
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@SystemApi
+ @Deprecated
public boolean iccCloseLogicalChannelBySlot(int slotIndex, int channel) {
try {
ITelephony telephony = getITelephony();
@@ -6898,6 +7043,45 @@ public class TelephonyManager {
}
/**
+ * Closes a previously opened logical channel to the ICC card using the physical slot index and
+ * port index.
+ *
+ * Use this method when no subscriptions are available on the SIM and the operation must be
+ * performed using the physical slot index and port index.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CCHC command.
+ *
+ * @param slotIndex the physical slot index of the ICC card
+ * @param portIndex The port index is an enumeration of the ports available on the UICC.
+ * Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+ * @param channel is the channel id to be closed as returned by a successful
+ * iccOpenLogicalChannel.
+ *
+ * @throws IllegalStateException if the Telephony process is not currently available or modem
+ * currently can't process this command.
+ * @throws IllegalArgumentException if invalid arguments are passed.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ public void iccCloseLogicalChannelByPort(int slotIndex, int portIndex, int channel) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ IccLogicalChannelRequest request = new IccLogicalChannelRequest();
+ request.slotIndex = slotIndex;
+ request.portIndex = portIndex;
+ request.channel = channel;
+ telephony.iccCloseLogicalChannel(request);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Closes a previously opened logical channel to the ICC card.
*
* Input parameters equivalent to TS 27.007 AT+CCHC command.
@@ -6910,6 +7094,7 @@ public class TelephonyManager {
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean iccCloseLogicalChannel(int channel) {
return iccCloseLogicalChannel(getSubId(), channel);
}
@@ -6968,10 +7153,14 @@ public class TelephonyManager {
* @return The APDU response from the ICC card with the status appended at the end, or null if
* there is an issue connecting to the Telephony service.
* @hide
+ * @deprecated instead use
+ * {@link #iccTransmitApduLogicalChannelByPort(int, int, int, int, int, int, int, int, String)}
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@SystemApi
@Nullable
+ @Deprecated
public String iccTransmitApduLogicalChannelBySlot(int slotIndex, int channel, int cla,
int instruction, int p1, int p2, int p3, @Nullable String data) {
try {
@@ -6987,6 +7176,50 @@ public class TelephonyManager {
}
/**
+ * Transmit an APDU to the ICC card over a logical channel using the physical slot index.
+ *
+ * Use this method when no subscriptions are available on the SIM and the operation must be
+ * performed using the physical slot index.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CGLA command.
+ *
+ * @param slotIndex the physical slot index of the ICC card
+ * @param portIndex The port index is an enumeration of the ports available on the UICC.
+ * Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+ * @param channel is the channel id to be closed as returned by a successful
+ * iccOpenLogicalChannel.
+ * @param cla Class of the APDU command.
+ * @param instruction Instruction of the APDU command.
+ * @param p1 P1 value of the APDU command.
+ * @param p2 P2 value of the APDU command.
+ * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+ * is sent to the SIM.
+ * @param data Data to be sent with the APDU.
+ * @return The APDU response from the ICC card with the status appended at the end, or null if
+ * there is an issue connecting to the Telephony service.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ @NonNull
+ public String iccTransmitApduLogicalChannelByPort(int slotIndex, int portIndex, int channel,
+ int cla, int instruction, int p1, int p2, int p3, @Nullable String data) {
+ String response;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ response = telephony.iccTransmitApduLogicalChannelByPort(slotIndex, portIndex,
+ channel, cla, instruction, p1, p2, p3, data);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowAsRuntimeException();
+ }
+ return response;
+ }
+
+ /**
* Transmit an APDU to the ICC card over a logical channel.
*
* Input parameters equivalent to TS 27.007 AT+CGLA command.
@@ -7007,6 +7240,7 @@ public class TelephonyManager {
* @return The APDU response from the ICC card with the status appended at
* the end.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String iccTransmitApduLogicalChannel(int channel, int cla,
int instruction, int p1, int p2, int p3, String data) {
return iccTransmitApduLogicalChannel(getSubId(), channel, cla,
@@ -7071,10 +7305,14 @@ public class TelephonyManager {
* @return The APDU response from the ICC card with the status appended at
* the end.
* @hide
+ * @deprecated instead use
+ * {@link #iccTransmitApduBasicChannelByPort(int, int, int, int, int, int, int, String)}
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@SystemApi
@NonNull
+ @Deprecated
public String iccTransmitApduBasicChannelBySlot(int slotIndex, int cla, int instruction, int p1,
int p2, int p3, @Nullable String data) {
try {
@@ -7090,6 +7328,47 @@ public class TelephonyManager {
}
/**
+ * Transmit an APDU to the ICC card over the basic channel using the physical slot index.
+ *
+ * Use this method when no subscriptions are available on the SIM and the operation must be
+ * performed using the physical slot index.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CSIM command.
+ *
+ * @param slotIndex the physical slot index of the ICC card to target
+ * @param portIndex The port index is an enumeration of the ports available on the UICC.
+ * Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+ * @param cla Class of the APDU command.
+ * @param instruction Instruction of the APDU command.
+ * @param p1 P1 value of the APDU command.
+ * @param p2 P2 value of the APDU command.
+ * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+ * is sent to the SIM.
+ * @param data Data to be sent with the APDU.
+ * @return The APDU response from the ICC card with the status appended at
+ * the end.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ @NonNull
+ public String iccTransmitApduBasicChannelByPort(int slotIndex, int portIndex, int cla,
+ int instruction, int p1, int p2, int p3, @Nullable String data) {
+ String response;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ response = telephony.iccTransmitApduBasicChannelByPort(slotIndex, portIndex,
+ getOpPackageName(), cla, instruction, p1, p2, p3, data);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowAsRuntimeException();
+ }
+ return response;
+ }
+ /**
* Transmit an APDU to the ICC card over the basic channel.
*
* Input parameters equivalent to TS 27.007 AT+CSIM command.
@@ -7108,6 +7387,7 @@ public class TelephonyManager {
* @return The APDU response from the ICC card with the status appended at
* the end.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String iccTransmitApduBasicChannel(int cla,
int instruction, int p1, int p2, int p3, String data) {
return iccTransmitApduBasicChannel(getSubId(), cla,
@@ -7163,6 +7443,7 @@ public class TelephonyManager {
* @param filePath
* @return The APDU response.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
String filePath) {
return iccExchangeSimIO(getSubId(), fileID, command, p1, p2, p3, filePath);
@@ -7211,6 +7492,7 @@ public class TelephonyManager {
* with the last 4 bytes being the status word. If the command fails,
* returns an empty string.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String sendEnvelopeWithStatus(String content) {
return sendEnvelopeWithStatus(getSubId(), content);
}
@@ -7374,6 +7656,7 @@ public class TelephonyManager {
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean resetRadioConfig() {
try {
ITelephony telephony = getITelephony();
@@ -7401,6 +7684,7 @@ public class TelephonyManager {
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean rebootRadio() {
try {
ITelephony telephony = getITelephony();
@@ -7422,6 +7706,7 @@ public class TelephonyManager {
* subscription ID is returned. Otherwise, the default subscription ID will be returned.
*
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int getSubscriptionId() {
return getSubId();
}
@@ -7532,6 +7817,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void requestNumberVerification(@NonNull PhoneNumberRange range, long timeoutMillis,
@NonNull @CallbackExecutor Executor executor,
@NonNull NumberVerificationCallback callback) {
@@ -7745,6 +8031,7 @@ public class TelephonyManager {
@Nullable
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String getIsimIst() {
try {
IPhoneSubInfo info = getSubscriberInfoService();
@@ -7829,6 +8116,7 @@ public class TelephonyManager {
// TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
// READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
// it's not public API.
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String getIccAuthentication(int appType, int authType, String data) {
return getIccAuthentication(getSubId(), appType, authType, data);
}
@@ -7882,6 +8170,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String[] getForbiddenPlmns() {
return getForbiddenPlmns(getSubId(), APPTYPE_USIM);
}
@@ -7931,6 +8220,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int setForbiddenPlmns(@NonNull List<String> fplmns) {
try {
ITelephony telephony = getITelephony();
@@ -7975,6 +8265,7 @@ public class TelephonyManager {
@SystemApi
@WorkerThread
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public void resetIms(int slotIndex) {
try {
ITelephony telephony = getITelephony();
@@ -8409,6 +8700,7 @@ public class TelephonyManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @NetworkTypeBitMask long getAllowedNetworkTypesBitmask() {
try {
ITelephony telephony = getITelephony();
@@ -8460,6 +8752,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void setNetworkSelectionModeAutomatic() {
try {
ITelephony telephony = getITelephony();
@@ -8548,6 +8841,7 @@ public class TelephonyManager {
android.Manifest.permission.MODIFY_PHONE_STATE,
Manifest.permission.ACCESS_FINE_LOCATION
})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public NetworkScan requestNetworkScan(
NetworkScanRequest request, Executor executor,
TelephonyScanManager.NetworkScanCallback callback) {
@@ -8640,6 +8934,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) {
return setNetworkSelectionModeManual(
new OperatorInfo(
@@ -8669,6 +8964,7 @@ public class TelephonyManager {
* @return {@code true} on success; {@code false} on any failure.
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean setNetworkSelectionModeManual(@NonNull String operatorNumeric,
boolean persistSelection, @AccessNetworkConstants.RadioAccessNetworkType int ran) {
return setNetworkSelectionModeManual(new OperatorInfo("" /* operatorAlphaLong */,
@@ -8724,6 +9020,7 @@ public class TelephonyManager {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PRECISE_PHONE_STATE
})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @NetworkSelectionMode int getNetworkSelectionMode() {
int mode = NETWORK_SELECTION_MODE_UNKNOWN;
try {
@@ -8749,6 +9046,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // No support carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @NonNull String getManualNetworkSelectionPlmn() {
try {
ITelephony telephony = getITelephony();
@@ -8778,6 +9076,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
public boolean isInEmergencySmsMode() {
try {
ITelephony telephony = getITelephony();
@@ -9087,6 +9386,7 @@ public class TelephonyManager {
*
* @return true on success; false on any failure.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean setPreferredNetworkTypeToGlobal() {
return setPreferredNetworkTypeToGlobal(getSubId());
}
@@ -9114,6 +9414,7 @@ public class TelephonyManager {
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isTetheringApnRequired() {
return isTetheringApnRequired(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
}
@@ -9163,6 +9464,7 @@ public class TelephonyManager {
*
* @return true if the app has carrier privileges.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean hasCarrierPrivileges() {
return hasCarrierPrivileges(getSubId());
}
@@ -9209,6 +9511,7 @@ public class TelephonyManager {
* @param brand The brand name to display/set.
* @return true if the operation was executed correctly.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean setOperatorBrandOverride(String brand) {
return setOperatorBrandOverride(getSubId(), brand);
}
@@ -9311,6 +9614,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getCdmaMdn() {
return getCdmaMdn(getSubId());
}
@@ -9318,6 +9622,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getCdmaMdn(int subId) {
try {
ITelephony telephony = getITelephony();
@@ -9334,6 +9639,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getCdmaMin() {
return getCdmaMin(getSubId());
}
@@ -9341,6 +9647,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getCdmaMin(int subId) {
try {
ITelephony telephony = getITelephony();
@@ -9357,6 +9664,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int checkCarrierPrivilegesForPackage(String pkgName) {
try {
ITelephony telephony = getITelephony();
@@ -9373,6 +9681,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) {
try {
ITelephony telephony = getITelephony();
@@ -9388,6 +9697,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public List<String> getCarrierPackageNamesForIntent(Intent intent) {
return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId());
}
@@ -9395,6 +9705,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public List<String> getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) {
try {
ITelephony telephony = getITelephony();
@@ -9408,6 +9719,57 @@ public class TelephonyManager {
return null;
}
+ /**
+ * Returns the package name that provides the {@link CarrierService} implementation for the
+ * current subscription, or {@code null} if no package with carrier privileges declares one.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, then the provided
+ * subscription ID is used. Otherwise, the default subscription ID will be used.
+ *
+ * @return The system-selected package that provides the {@link CarrierService} implementation
+ * for the current subscription, or {@code null} if none is resolved
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @Nullable String getCarrierServicePackageName() {
+ // TODO(b/205736323) plumb this through to CarrierPrivilegesTracker, which will cache the
+ // value instead of re-querying every time.
+ List<String> carrierServicePackages =
+ getCarrierPackageNamesForIntent(
+ new Intent(CarrierService.CARRIER_SERVICE_INTERFACE));
+ if (carrierServicePackages != null && !carrierServicePackages.isEmpty()) {
+ return carrierServicePackages.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the package name that provides the {@link CarrierService} implementation for the
+ * specified {@code logicalSlotIndex}, or {@code null} if no package with carrier privileges
+ * declares one.
+ *
+ * @param logicalSlotIndex The slot index to fetch the {@link CarrierService} package for
+ * @return The system-selected package that provides the {@link CarrierService} implementation
+ * for the slot, or {@code null} if none is resolved
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @Nullable String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) {
+ // TODO(b/205736323) plumb this through to CarrierPrivilegesTracker, which will cache the
+ // value instead of re-querying every time.
+ List<String> carrierServicePackages =
+ getCarrierPackageNamesForIntentAndPhone(
+ new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), logicalSlotIndex);
+ if (carrierServicePackages != null && !carrierServicePackages.isEmpty()) {
+ return carrierServicePackages.get(0);
+ }
+ return null;
+ }
+
/** @hide */
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public List<String> getPackagesWithCarrierPrivileges() {
@@ -9431,6 +9793,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@NonNull
public List<String> getCarrierPrivilegedPackagesForAllActiveSubscriptions() {
try {
@@ -9477,6 +9840,7 @@ public class TelephonyManager {
* @throws SecurityException if the caller does not have the permission.
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void setCallComposerStatus(@CallComposerStatus int status) {
if (status > CALL_COMPOSER_STATUS_ON
|| status < CALL_COMPOSER_STATUS_OFF) {
@@ -9505,6 +9869,7 @@ public class TelephonyManager {
* {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}.
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public @CallComposerStatus int getCallComposerStatus() {
try {
ITelephony telephony = getITelephony();
@@ -9521,6 +9886,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@SuppressLint("RequiresPermission")
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void dial(String number) {
try {
ITelephony telephony = getITelephony();
@@ -9653,6 +10019,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean supplyPin(String pin) {
try {
ITelephony telephony = getITelephony();
@@ -9667,6 +10034,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean supplyPuk(String puk, String pin) {
try {
ITelephony telephony = getITelephony();
@@ -9732,6 +10100,7 @@ public class TelephonyManager {
@SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public PinResult supplyIccLockPin(@NonNull String pin) {
try {
ITelephony telephony = getITelephony();
@@ -9767,6 +10136,7 @@ public class TelephonyManager {
@SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public PinResult supplyIccLockPuk(@NonNull String puk, @NonNull String pin) {
try {
ITelephony telephony = getITelephony();
@@ -9836,6 +10206,7 @@ public class TelephonyManager {
* @param handler the {@link Handler} to run the request on.
*/
@RequiresPermission(android.Manifest.permission.CALL_PHONE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void sendUssdRequest(String ussdRequest,
final UssdResponseCallback callback, Handler handler) {
checkNotNull(callback, "UssdResponseCallback cannot be null.");
@@ -9878,6 +10249,7 @@ public class TelephonyManager {
*
* @return {@code true} if simultaneous voice and data supported, and {@code false} otherwise.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isConcurrentVoiceAndDataSupported() {
try {
ITelephony telephony = getITelephony();
@@ -9920,6 +10292,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void toggleRadioOnOff() {
try {
ITelephony telephony = getITelephony();
@@ -9933,6 +10306,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean setRadio(boolean turnOn) {
try {
ITelephony telephony = getITelephony();
@@ -9947,6 +10321,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean setRadioPower(boolean turnOn) {
try {
ITelephony telephony = getITelephony();
@@ -9968,6 +10343,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void shutdownAllRadios() {
try {
ITelephony telephony = getITelephony();
@@ -9988,6 +10364,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean isAnyRadioPoweredOn() {
try {
ITelephony telephony = getITelephony();
@@ -10034,6 +10411,7 @@ public class TelephonyManager {
@SystemApi
@RequiresPermission(anyOf = {android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PHONE_STATE})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @RadioPowerState int getRadioPowerState() {
try {
ITelephony telephony = getITelephony();
@@ -10060,6 +10438,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean enableDataConnectivity() {
try {
ITelephony telephony = getITelephony();
@@ -10074,6 +10453,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean disableDataConnectivity() {
try {
ITelephony telephony = getITelephony();
@@ -10087,6 +10467,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isDataConnectivityPossible() {
try {
ITelephony telephony = getITelephony();
@@ -10101,6 +10482,7 @@ public class TelephonyManager {
/** @hide */
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean needsOtaServiceProvisioning() {
try {
ITelephony telephony = getITelephony();
@@ -10208,6 +10590,7 @@ public class TelephonyManager {
android.Manifest.permission.MODIFY_PHONE_STATE,
android.Manifest.permission.READ_PHONE_STATE,
android.Manifest.permission.READ_BASIC_PHONE_STATE})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isDataEnabled() {
try {
return isDataEnabledForReason(DATA_ENABLED_REASON_USER);
@@ -10237,6 +10620,7 @@ public class TelephonyManager {
@RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
android.Manifest.permission.READ_PHONE_STATE,
android.Manifest.permission.READ_BASIC_PHONE_STATE})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isDataRoamingEnabled() {
boolean isDataRoamingEnabled = false;
try {
@@ -10274,6 +10658,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public @CdmaRoamingMode int getCdmaRoamingMode() {
int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT;
try {
@@ -10315,6 +10700,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public void setCdmaRoamingMode(@CdmaRoamingMode int mode) {
if (getPhoneType() != PHONE_TYPE_CDMA) {
throw new IllegalStateException("Phone does not support CDMA.");
@@ -10382,6 +10768,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public @CdmaSubscription int getCdmaSubscriptionMode() {
int mode = CDMA_SUBSCRIPTION_RUIM_SIM;
try {
@@ -10419,6 +10806,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public void setCdmaSubscriptionMode(@CdmaSubscription int mode) {
if (getPhoneType() != PHONE_TYPE_CDMA) {
throw new IllegalStateException("Phone does not support CDMA.");
@@ -10453,6 +10841,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public void setDataRoamingEnabled(boolean isEnabled) {
try {
ITelephony telephony = getITelephony();
@@ -10550,6 +10939,7 @@ public class TelephonyManager {
*
* @return {@code true} if the DTMF tone length can be changed, and {@code false} otherwise.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean canChangeDtmfToneLength() {
try {
ITelephony telephony = getITelephony();
@@ -10613,6 +11003,7 @@ public class TelephonyManager {
*
* @return {@code true} if the device and carrier both support RTT, {@code false} otherwise.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public boolean isRttSupported() {
try {
ITelephony telephony = getITelephony();
@@ -10632,6 +11023,7 @@ public class TelephonyManager {
* @return {@code true} if the device supports hearing aid compatibility, and {@code false}
* otherwise.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean isHearingAidCompatibilitySupported() {
try {
ITelephony telephony = getITelephony();
@@ -10979,6 +11371,7 @@ public class TelephonyManager {
**/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public void setSimPowerState(@SimPowerState int state, @NonNull Executor executor,
@NonNull @SetSimPowerStateResult Consumer<Integer> callback) {
setSimPowerStateForSlot(getSlotIndex(), state, executor, callback);
@@ -11008,6 +11401,7 @@ public class TelephonyManager {
**/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public void setSimPowerStateForSlot(int slotIndex, @SimPowerState int state,
@NonNull Executor executor,
@NonNull @SetSimPowerStateResult Consumer<Integer> callback) {
@@ -11222,6 +11616,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
public @Nullable ComponentName getAndUpdateDefaultRespondViaMessageApplication() {
return SmsApplication.getDefaultRespondViaMessageApplication(mContext, true);
}
@@ -11234,6 +11629,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
public @Nullable ComponentName getDefaultRespondViaMessageApplication() {
return SmsApplication.getDefaultRespondViaMessageApplication(mContext, false);
}
@@ -11407,6 +11803,7 @@ public class TelephonyManager {
* permission.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int getSubscriptionId(@NonNull PhoneAccountHandle phoneAccountHandle) {
int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
try {
@@ -11473,6 +11870,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@Nullable public Locale getSimLocale() {
try {
final ITelephony telephony = getITelephony();
@@ -11667,6 +12065,7 @@ public class TelephonyManager {
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.ACCESS_COARSE_LOCATION
})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @Nullable ServiceState getServiceState() {
return getServiceState(false, false);
}
@@ -11757,6 +12156,7 @@ public class TelephonyManager {
* @return The URI for the ringtone to play when receiving a voicemail from a specific
* PhoneAccount. May be {@code null} if no ringtone is set.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public @Nullable Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
try {
ITelephony service = getITelephony();
@@ -11784,6 +12184,7 @@ public class TelephonyManager {
* @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
* instead.
*/
+ @Deprecated
public void setVoicemailRingtoneUri(PhoneAccountHandle phoneAccountHandle, Uri uri) {
try {
ITelephony service = getITelephony();
@@ -11802,6 +12203,7 @@ public class TelephonyManager {
* voicemail vibration setting.
* @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean isVoicemailVibrationEnabled(PhoneAccountHandle accountHandle) {
try {
ITelephony service = getITelephony();
@@ -11829,6 +12231,7 @@ public class TelephonyManager {
* @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
* instead.
*/
+ @Deprecated
public void setVoicemailVibrationEnabled(PhoneAccountHandle phoneAccountHandle,
boolean enabled) {
try {
@@ -11855,6 +12258,7 @@ public class TelephonyManager {
* @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
* subscription is unavailable or the carrier cannot be identified.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int getSimCarrierId() {
try {
ITelephony service = getITelephony();
@@ -11879,6 +12283,7 @@ public class TelephonyManager {
* @return Carrier name of the current subscription. Return {@code null} if the subscription is
* unavailable or the carrier cannot be identified.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @Nullable CharSequence getSimCarrierIdName() {
try {
ITelephony service = getITelephony();
@@ -11916,6 +12321,7 @@ public class TelephonyManager {
* Return {@link #UNKNOWN_CARRIER_ID} if the subscription is unavailable or the carrier cannot
* be identified.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int getSimSpecificCarrierId() {
try {
ITelephony service = getITelephony();
@@ -11941,6 +12347,7 @@ public class TelephonyManager {
* @return user-facing name of the subscription specific carrier id. Return {@code null} if the
* subscription is unavailable or the carrier cannot be identified.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @Nullable CharSequence getSimSpecificCarrierIdName() {
try {
ITelephony service = getITelephony();
@@ -11968,6 +12375,7 @@ public class TelephonyManager {
* @return matching carrier id from sim MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the
* subscription is unavailable or the carrier cannot be identified.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int getCarrierIdFromSimMccMnc() {
try {
ITelephony service = getITelephony();
@@ -12046,6 +12454,7 @@ public class TelephonyManager {
@Nullable
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String getAidForAppType(@UiccAppType int appType) {
return getAidForAppType(getSubId(), appType);
}
@@ -12108,6 +12517,7 @@ public class TelephonyManager {
* @hide
*/
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getCdmaPrlVersion() {
return getCdmaPrlVersion(getSubId());
}
@@ -12173,6 +12583,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
public int setAllowedCarriers(int slotIndex, List<CarrierIdentifier> carriers) {
if (carriers == null || !SubscriptionManager.isValidPhoneId(slotIndex)) {
return -1;
@@ -12296,6 +12707,7 @@ public class TelephonyManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@SetCarrierRestrictionResult
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
public int setCarrierRestrictionRules(@NonNull CarrierRestrictionRules rules) {
try {
ITelephony service = getITelephony();
@@ -12353,6 +12765,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
@Nullable
public CarrierRestrictionRules getCarrierRestrictionRules() {
try {
@@ -12412,6 +12825,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void setRadioEnabled(boolean enabled) {
try {
ITelephony service = getITelephony();
@@ -12533,6 +12947,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void reportDefaultNetworkStatus(boolean report) {
try {
ITelephony service = getITelephony();
@@ -12558,6 +12973,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public void resetAllCarrierActions() {
try {
ITelephony service = getITelephony();
@@ -12651,6 +13067,7 @@ public class TelephonyManager {
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public void setDataEnabledForReason(@DataEnabledReason int reason, boolean enabled) {
setDataEnabledForReason(getSubId(), reason, enabled);
}
@@ -12697,6 +13114,7 @@ public class TelephonyManager {
android.Manifest.permission.MODIFY_PHONE_STATE,
android.Manifest.permission.READ_BASIC_PHONE_STATE
})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isDataEnabledForReason(@DataEnabledReason int reason) {
return isDataEnabledForReason(getSubId(), reason);
}
@@ -12750,6 +13168,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean getEmergencyCallbackMode() {
return getEmergencyCallbackMode(getSubId());
}
@@ -12788,6 +13207,7 @@ public class TelephonyManager {
@SuppressAutoDoc // No support carrier privileges (b/72967236).
@RequiresPermission(anyOf = {android.Manifest.permission.READ_PRECISE_PHONE_STATE,
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean isManualNetworkSelectionAllowed() {
try {
ITelephony telephony = getITelephony();
@@ -12808,6 +13228,7 @@ public class TelephonyManager {
* @return the most recent cached signal strength info from the modem
*/
@Nullable
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public SignalStrength getSignalStrength() {
try {
ITelephony service = getITelephony();
@@ -12836,6 +13257,7 @@ public class TelephonyManager {
android.Manifest.permission.READ_PHONE_STATE,
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_BASIC_PHONE_STATE})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isDataConnectionAllowed() {
boolean retVal = false;
try {
@@ -12856,6 +13278,7 @@ public class TelephonyManager {
* data connections over the telephony network.
* <p>
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isDataCapable() {
if (mContext == null) return true;
return mContext.getResources().getBoolean(
@@ -13003,6 +13426,7 @@ public class TelephonyManager {
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean setOpportunisticNetworkState(boolean enable) {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
boolean ret = false;
@@ -13031,6 +13455,7 @@ public class TelephonyManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean isOpportunisticNetworkEnabled() {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
boolean isEnabled = false;
@@ -13258,6 +13683,7 @@ public class TelephonyManager {
@SystemApi
@TestApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @NetworkTypeBitMask long getSupportedRadioAccessFamily() {
try {
ITelephony telephony = getITelephony();
@@ -13293,6 +13719,7 @@ public class TelephonyManager {
* @hide
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@SystemApi
public void notifyOtaEmergencyNumberDbInstalled() {
try {
@@ -13319,6 +13746,7 @@ public class TelephonyManager {
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@SystemApi
public void updateOtaEmergencyNumberDbFilePath(
@NonNull ParcelFileDescriptor otaParcelFileDescriptor) {
@@ -13344,6 +13772,7 @@ public class TelephonyManager {
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@SystemApi
public void resetOtaEmergencyNumberDbFilePath() {
try {
@@ -13403,6 +13832,7 @@ public class TelephonyManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@NonNull
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList() {
Map<Integer, List<EmergencyNumber>> emergencyNumberList = new HashMap<>();
try {
@@ -13458,6 +13888,7 @@ public class TelephonyManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@NonNull
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList(
@EmergencyServiceCategories int categories) {
Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>();
@@ -13523,6 +13954,7 @@ public class TelephonyManager {
* SIM card(s), Android database, modem, network or defaults; {@code false} otherwise.
* @throws IllegalStateException if the Telephony process is not currently available.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean isEmergencyNumber(@NonNull String number) {
try {
ITelephony telephony = getITelephony();
@@ -13562,6 +13994,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean isPotentialEmergencyNumber(@NonNull String number) {
try {
ITelephony telephony = getITelephony();
@@ -13587,6 +14020,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public int getEmergencyNumberDbVersion() {
try {
ITelephony telephony = getITelephony();
@@ -13727,6 +14161,7 @@ public class TelephonyManager {
* See {@link TelephonyManager.SetOpportunisticSubscriptionResult}
* for more details. Pass null if don't care about the result.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public void setPreferredOpportunisticDataSubscription(int subId, boolean needValidation,
@Nullable @CallbackExecutor Executor executor, @Nullable Consumer<Integer> callback) {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
@@ -13790,6 +14225,7 @@ public class TelephonyManager {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PHONE_STATE
})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public int getPreferredOpportunisticDataSubscription() {
String packageName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
String attributionTag = mContext != null ? mContext.getAttributionTag() : null;
@@ -13825,6 +14261,7 @@ public class TelephonyManager {
*
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void updateAvailableNetworks(@NonNull List<AvailableNetworkInfo> availableNetworks,
@Nullable @CallbackExecutor Executor executor,
@UpdateAvailableNetworksResult @Nullable Consumer<Integer> callback) {
@@ -13975,6 +14412,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
public void setMultiSimCarrierRestriction(boolean isMultiSimCarrierRestricted) {
try {
ITelephony service = getITelephony();
@@ -14028,6 +14466,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@IsMultiSimSupportedResult
public int isMultiSimSupported() {
if (getSupportedModemCount() < 2) {
@@ -14058,6 +14497,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public void switchMultiSimConfig(int numOfSims) {
try {
ITelephony telephony = getITelephony();
@@ -14083,6 +14523,7 @@ public class TelephonyManager {
* configurations, otherwise return {@code false}.
*/
@RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean doesSwitchMultiSimConfigTriggerReboot() {
try {
ITelephony service = getITelephony();
@@ -14135,6 +14576,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @CarrierPrivilegeStatus int getCarrierPrivilegeStatus(int uid) {
try {
ITelephony telephony = getITelephony();
@@ -14248,6 +14690,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isDataEnabledForApn(@ApnType int apnType) {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
try {
@@ -14269,6 +14712,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isApnMetered(@ApnType int apnType) {
try {
ITelephony service = getITelephony();
@@ -14297,6 +14741,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void setSystemSelectionChannels(@NonNull List<RadioAccessSpecifier> specifiers,
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<Boolean> callback) {
@@ -14314,6 +14759,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void setSystemSelectionChannels(@NonNull List<RadioAccessSpecifier> specifiers) {
Objects.requireNonNull(specifiers, "Specifiers must not be null.");
setSystemSelectionChannelsInternal(specifiers, null, null);
@@ -14360,6 +14806,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @NonNull List<RadioAccessSpecifier> getSystemSelectionChannels() {
try {
ITelephony service = getITelephony();
@@ -14387,6 +14834,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean matchesCurrentSimOperator(@NonNull String mccmnc, @MvnoType int mvnoType,
@Nullable String mvnoMatchData) {
try {
@@ -14477,6 +14925,7 @@ public class TelephonyManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void getCallForwarding(@CallForwardingReason int callForwardingReason,
@NonNull Executor executor, @NonNull CallForwardingInfoCallback callback) {
if (callForwardingReason < CallForwardingInfo.REASON_UNCONDITIONAL
@@ -14552,6 +15001,7 @@ public class TelephonyManager {
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void setCallForwarding(@NonNull CallForwardingInfo callForwardingInfo,
@Nullable @CallbackExecutor Executor executor,
@Nullable @CallForwardingInfoCallback.CallForwardingError
@@ -14664,6 +15114,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void getCallWaitingStatus(@NonNull Executor executor,
@NonNull @CallWaitingStatus Consumer<Integer> resultListener) {
Objects.requireNonNull(executor);
@@ -14712,6 +15163,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void setCallWaitingEnabled(boolean enabled, @Nullable Executor executor,
@Nullable Consumer<Integer> resultListener) {
if (resultListener != null) {
@@ -14793,6 +15245,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public void setMobileDataPolicyEnabled(@MobileDataPolicy int policy, boolean enabled) {
try {
ITelephony service = getITelephony();
@@ -14813,6 +15266,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isMobileDataPolicyEnabled(@MobileDataPolicy int policy) {
try {
ITelephony service = getITelephony();
@@ -14847,6 +15301,7 @@ public class TelephonyManager {
*/
@WorkerThread
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@SystemApi
public boolean isIccLockEnabled() {
try {
@@ -14881,6 +15336,7 @@ public class TelephonyManager {
@SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public PinResult setIccLockEnabled(boolean enabled, @NonNull String pin) {
checkNotNull(pin, "setIccLockEnabled pin can't be null.");
try {
@@ -14922,6 +15378,7 @@ public class TelephonyManager {
@SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public PinResult changeIccLockPin(@NonNull String oldPin, @NonNull String newPin) {
checkNotNull(oldPin, "changeIccLockPin oldPin can't be null.");
checkNotNull(newPin, "changeIccLockPin newPin can't be null.");
@@ -15314,6 +15771,7 @@ public class TelephonyManager {
*
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @NonNull List<String> getEquivalentHomePlmns() {
try {
ITelephony telephony = getITelephony();
@@ -15427,6 +15885,7 @@ public class TelephonyManager {
* @param capability the name of the capability to check for
* @return the availability of the capability
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean isRadioInterfaceCapabilitySupported(
@NonNull @RadioInterfaceCapability String capability) {
try {
@@ -15548,6 +16007,7 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@ThermalMitigationResult
public int sendThermalMitigationRequest(
@NonNull ThermalMitigationRequest thermalMitigationRequest) {
@@ -15819,6 +16279,7 @@ public class TelephonyManager {
@WorkerThread
@RequiresPermission(anyOf = {android.Manifest.permission.MODIFY_PHONE_STATE,
Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public void bootstrapAuthenticationRequest(
@UiccAppTypeExt int appType, @NonNull Uri nafId,
@NonNull UaSecurityProtocolIdentifier securityProtocol,
@@ -15913,6 +16374,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void setSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) {
Objects.requireNonNull(request, "request must not be null");
@@ -15942,6 +16404,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public void clearSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) {
Objects.requireNonNull(request, "request must not be null");
@@ -16202,4 +16665,76 @@ public class TelephonyManager {
}
return null;
}
+
+ /**
+ * Callback to listen for when the set of packages with carrier privileges for a SIM changes.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface CarrierPrivilegesListener {
+ /**
+ * Called when the set of packages with carrier privileges has changed.
+ *
+ * <p>Of note, this callback will <b>not</b> be fired if a carrier triggers a SIM profile
+ * switch and the same set of packages remains privileged after the switch.
+ *
+ * <p>At registration, the callback will receive the current set of privileged packages.
+ *
+ * @param privilegedPackageNames The updated set of package names that have carrier
+ * privileges
+ * @param privilegedUids The updated set of UIDs that have carrier privileges
+ */
+ void onCarrierPrivilegesChanged(
+ @NonNull List<String> privilegedPackageNames, @NonNull int[] privilegedUids);
+ }
+
+ /**
+ * Registers a {@link CarrierPrivilegesListener} on the given {@code logicalSlotIndex} to
+ * receive callbacks when the set of packages with carrier privileges changes. The callback will
+ * immediately be called with the latest state.
+ *
+ * @param logicalSlotIndex The SIM slot to listen on
+ * @param executor The executor where {@code listener} will be invoked
+ * @param listener The callback to register
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void addCarrierPrivilegesListener(
+ int logicalSlotIndex,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CarrierPrivilegesListener listener) {
+ if (mContext == null) {
+ throw new IllegalStateException("Telephony service is null");
+ } else if (executor == null || listener == null) {
+ throw new IllegalArgumentException(
+ "CarrierPrivilegesListener and executor must be non-null");
+ }
+ mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+ if (mTelephonyRegistryMgr == null) {
+ throw new IllegalStateException("Telephony registry service is null");
+ }
+ mTelephonyRegistryMgr.addCarrierPrivilegesListener(logicalSlotIndex, executor, listener);
+ }
+
+ /**
+ * Unregisters an existing {@link CarrierPrivilegesListener}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void removeCarrierPrivilegesListener(@NonNull CarrierPrivilegesListener listener) {
+ if (mContext == null) {
+ throw new IllegalStateException("Telephony service is null");
+ } else if (listener == null) {
+ throw new IllegalArgumentException("CarrierPrivilegesListener must be non-null");
+ }
+ mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+ if (mTelephonyRegistryMgr == null) {
+ throw new IllegalStateException("Telephony registry service is null");
+ }
+ mTelephonyRegistryMgr.removeCarrierPrivilegesListener(listener);
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 122662d3d7c8..e0c529848aae 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -19,6 +19,8 @@ package android.telephony;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -42,6 +44,7 @@ import java.util.concurrent.Executor;
/**
* Manages the radio access network scan requests and callbacks.
*/
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public final class TelephonyScanManager {
private static final String TAG = "TelephonyScanManager";
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 9ed230aebfbb..4ff59b568657 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -521,11 +521,11 @@ public class ApnSetting implements Parcelable {
private final boolean mAlwaysOn;
/**
- * Returns the MTU size of the IPv4 mobile interface to which the APN connected. Note this value
- * is used only if MTU size is not provided in {@link DataCallResponse}.
+ * Returns the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought
+ * up by this APN setting. Note this value will only be used when MTU size is not provided
+ * in {@link DataCallResponse#getMtuV4()} during network bring up.
*
- * @return the MTU size of the APN
- * @hide
+ * @return the MTU size in bytes of the route.
*/
public int getMtuV4() {
return mMtuV4;
@@ -533,10 +533,10 @@ public class ApnSetting implements Parcelable {
/**
* Returns the MTU size of the IPv6 mobile interface to which the APN connected. Note this value
- * is used only if MTU size is not provided in {@link DataCallResponse}.
+ * will only be used when MTU size is not provided in {@link DataCallResponse#getMtuV6()}
+ * during network bring up.
*
- * @return the MTU size of the APN
- * @hide
+ * @return the MTU size in bytes of the route.
*/
public int getMtuV6() {
return mMtuV6;
@@ -1753,25 +1753,25 @@ public class ApnSetting implements Parcelable {
}
/**
- * Set the MTU size of the IPv4 mobile interface to which the APN connected. Note this value
- * is used only if MTU size is not provided in {@link DataCallResponse}.
+ * Set the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought
+ * up by this APN setting. Note this value will only be used when MTU size is not provided
+ * in {@link DataCallResponse#getMtuV4()} during network bring up.
*
- * @param mtuV4 the MTU size to set for the APN
- * @hide
+ * @param mtuV4 the MTU size in bytes of the route.
*/
- public Builder setMtuV4(int mtuV4) {
+ public @NonNull Builder setMtuV4(int mtuV4) {
this.mMtuV4 = mtuV4;
return this;
}
/**
- * Set the MTU size of the IPv6 mobile interface to which the APN connected. Note this value
- * is used only if MTU size is not provided in {@link DataCallResponse}.
+ * Set the default MTU (Maximum Transmission Unit) size in bytes of the IPv6 routes brought
+ * up by this APN setting. Note this value will only be used when MTU size is not provided
+ * in {@link DataCallResponse#getMtuV6()} during network bring up.
*
- * @param mtuV6 the MTU size to set for the APN
- * @hide
+ * @param mtuV6 the MTU size in bytes of the route.
*/
- public Builder setMtuV6(int mtuV6) {
+ public @NonNull Builder setMtuV6(int mtuV6) {
this.mMtuV6 = mtuV6;
return this;
}
@@ -1779,15 +1779,25 @@ public class ApnSetting implements Parcelable {
/**
* Sets the profile id to which the APN saved in modem.
*
- * @param profileId the profile id to set for the APN
- * @hide
+ * @param profileId the profile id to set for the APN.
*/
- public Builder setProfileId(int profileId) {
+ public @NonNull Builder setProfileId(int profileId) {
this.mProfileId = profileId;
return this;
}
/**
+ * Set if the APN setting should be persistent/non-persistent in modem.
+ *
+ * @param isPersistent {@code true} if this APN setting should be persistent/non-persistent
+ * in modem.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setPersistent(boolean isPersistent) {
+ return setModemCognitive(isPersistent);
+ }
+
+ /**
* Sets if the APN setting is to be set in modem.
*
* @param modemCognitive if the APN setting is to be set in modem
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index 479f057eda9c..a166a5d6404c 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -38,8 +38,10 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
- * Description of a mobile data profile used for establishing
- * data connections.
+ * Description of a mobile data profile used for establishing data networks. The data profile
+ * consist an {@link ApnSetting} which is needed for 2G/3G/4G networks bring up, and a
+ * {@link TrafficDescriptor} contains additional information that can be used for 5G standalone
+ * network bring up.
*
* @hide
*/
@@ -113,7 +115,9 @@ public final class DataProfile implements Parcelable {
/**
* @return Id of the data profile.
+ * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getProfileId()} instead.
*/
+ @Deprecated
public int getProfileId() {
if (mApnSetting != null) {
return mApnSetting.getProfileId();
@@ -124,9 +128,10 @@ public final class DataProfile implements Parcelable {
/**
* @return The APN (Access Point Name) to establish data connection. This is a string
* specifically defined by the carrier.
+ * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getApnName()} instead.
*/
- @NonNull
- public String getApn() {
+ @Deprecated
+ public @NonNull String getApn() {
if (mApnSetting != null) {
return TextUtils.emptyIfNull(mApnSetting.getApnName());
}
@@ -135,7 +140,9 @@ public final class DataProfile implements Parcelable {
/**
* @return The connection protocol defined in 3GPP TS 27.007 section 10.1.1.
+ * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getProtocol()} instead.
*/
+ @Deprecated
public @ProtocolType int getProtocolType() {
if (mApnSetting != null) {
return mApnSetting.getProtocol();
@@ -145,7 +152,9 @@ public final class DataProfile implements Parcelable {
/**
* @return The authentication protocol used for this PDP context.
+ * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getAuthType()} instead.
*/
+ @Deprecated
public @AuthType int getAuthType() {
if (mApnSetting != null) {
return mApnSetting.getAuthType();
@@ -154,10 +163,11 @@ public final class DataProfile implements Parcelable {
}
/**
- * @return The username for APN. Can be null.
+ * @return The username for APN.
+ * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getUser()} instead.
*/
- @Nullable
- public String getUserName() {
+ @Deprecated
+ public @Nullable String getUserName() {
if (mApnSetting != null) {
return mApnSetting.getUser();
}
@@ -165,10 +175,11 @@ public final class DataProfile implements Parcelable {
}
/**
- * @return The password for APN. Can be null.
+ * @return The password for APN.
+ * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getPassword()} instead.
*/
- @Nullable
- public String getPassword() {
+ @Deprecated
+ public @Nullable String getPassword() {
if (mApnSetting != null) {
return mApnSetting.getPassword();
}
@@ -232,7 +243,9 @@ public final class DataProfile implements Parcelable {
/**
* @return The supported APN types bitmask.
+ * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getApnTypeBitmask()} instead.
*/
+ @Deprecated
public @ApnType int getSupportedApnTypesBitmask() {
if (mApnSetting != null) {
return mApnSetting.getApnTypeBitmask();
@@ -242,7 +255,9 @@ public final class DataProfile implements Parcelable {
/**
* @return The connection protocol on roaming network defined in 3GPP TS 27.007 section 10.1.1.
+ * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getRoamingProtocol()} instead.
*/
+ @Deprecated
public @ProtocolType int getRoamingProtocolType() {
if (mApnSetting != null) {
return mApnSetting.getRoamingProtocol();
@@ -252,7 +267,10 @@ public final class DataProfile implements Parcelable {
/**
* @return The bearer bitmask indicating the applicable networks for this data profile.
+ * @deprecated use {@link #getApnSetting()} and {@link ApnSetting#getNetworkTypeBitmask()}
+ * instead.
*/
+ @Deprecated
public @NetworkTypeBitMask int getBearerBitmask() {
if (mApnSetting != null) {
return mApnSetting.getNetworkTypeBitmask();
@@ -262,7 +280,8 @@ public final class DataProfile implements Parcelable {
/**
* @return The maximum transmission unit (MTU) size in bytes.
- * @deprecated use {@link #getMtuV4} or {@link #getMtuV6} instead.
+ * @deprecated use {@link #getApnSetting()} and {@link ApnSetting#getMtuV4()}/
+ * {@link ApnSetting#getMtuV6()} instead.
*/
@Deprecated
public int getMtu() {
@@ -272,7 +291,9 @@ public final class DataProfile implements Parcelable {
/**
* This replaces the deprecated method getMtu.
* @return The maximum transmission unit (MTU) size in bytes, for IPv4.
+ * @deprecated use {@link #getApnSetting()} and {@link ApnSetting#getMtuV4()} instead.
*/
+ @Deprecated
public int getMtuV4() {
if (mApnSetting != null) {
return mApnSetting.getMtuV4();
@@ -282,7 +303,9 @@ public final class DataProfile implements Parcelable {
/**
* @return The maximum transmission unit (MTU) size in bytes, for IPv6.
+ * @deprecated use {@link #getApnSetting()} and {@link ApnSetting#getMtuV6()} instead.
*/
+ @Deprecated
public int getMtuV6() {
if (mApnSetting != null) {
return mApnSetting.getMtuV6();
@@ -292,7 +315,9 @@ public final class DataProfile implements Parcelable {
/**
* @return {@code true} if modem must persist this data profile.
+ * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#isPersistent()} instead.
*/
+ @Deprecated
public boolean isPersistent() {
if (mApnSetting != null) {
return mApnSetting.isPersistent();
@@ -320,16 +345,16 @@ public final class DataProfile implements Parcelable {
}
/**
- * @return The APN setting
- * @hide TODO: Remove before T is released.
+ * @return The APN setting {@link ApnSetting}, which is used to establish data network on
+ * 2G/3G/4G.
*/
public @Nullable ApnSetting getApnSetting() {
return mApnSetting;
}
/**
- * @return The traffic descriptor
- * @hide TODO: Remove before T is released.
+ * @return The traffic descriptor {@link TrafficDescriptor}, which can be used to establish
+ * data network on 5G.
*/
public @Nullable TrafficDescriptor getTrafficDescriptor() {
return mTrafficDescriptor;
@@ -539,7 +564,10 @@ public final class DataProfile implements Parcelable {
*
* @param profileId Network domain.
* @return The same instance of the builder.
+ * @deprecated use {@link #setApnSetting(ApnSetting)} and
+ * {@link ApnSetting.Builder#setProfileId(int)} instead.
*/
+ @Deprecated
public @NonNull Builder setProfileId(int profileId) {
mProfileId = profileId;
return this;
@@ -551,7 +579,10 @@ public final class DataProfile implements Parcelable {
*
* @param apn Access point name
* @return The same instance of the builder.
+ * @deprecated use {@link #setApnSetting(ApnSetting)} and
+ * {@link ApnSetting.Builder#setApnName(String)} instead.
*/
+ @Deprecated
public @NonNull Builder setApn(@NonNull String apn) {
mApn = apn;
return this;
@@ -562,7 +593,10 @@ public final class DataProfile implements Parcelable {
*
* @param protocolType The connection protocol defined in 3GPP TS 27.007 section 10.1.1.
* @return The same instance of the builder.
+ * @deprecated use {@link #setApnSetting(ApnSetting)} and
+ * {@link ApnSetting.Builder#setProtocol(int)} instead.
*/
+ @Deprecated
public @NonNull Builder setProtocolType(@ProtocolType int protocolType) {
mProtocolType = protocolType;
return this;
@@ -573,7 +607,10 @@ public final class DataProfile implements Parcelable {
*
* @param authType The authentication type
* @return The same instance of the builder.
+ * @deprecated use {@link #setApnSetting(ApnSetting)} and
+ * {@link ApnSetting.Builder#setAuthType(int)} instead.
*/
+ @Deprecated
public @NonNull Builder setAuthType(@AuthType int authType) {
mAuthType = authType;
return this;
@@ -584,7 +621,10 @@ public final class DataProfile implements Parcelable {
*
* @param userName The user name
* @return The same instance of the builder.
+ * @deprecated use {@link #setApnSetting(ApnSetting)} and
+ * {@link ApnSetting.Builder#setUser(String)} instead.
*/
+ @Deprecated
public @NonNull Builder setUserName(@NonNull String userName) {
mUserName = userName;
return this;
@@ -595,7 +635,10 @@ public final class DataProfile implements Parcelable {
*
* @param password The password
* @return The same instance of the builder.
+ * @deprecated use {@link #setApnSetting(ApnSetting)} and
+ * {@link ApnSetting.Builder#setPassword(String)} (int)} instead.
*/
+ @Deprecated
public @NonNull Builder setPassword(@NonNull String password) {
mPassword = password;
return this;
@@ -628,7 +671,10 @@ public final class DataProfile implements Parcelable {
*
* @param supportedApnTypesBitmask The supported APN types bitmask.
* @return The same instance of the builder.
+ * @deprecated use {@link #setApnSetting(ApnSetting)} and
+ * {@link ApnSetting.Builder#setApnTypeBitmask(int)} instead.
*/
+ @Deprecated
public @NonNull Builder setSupportedApnTypesBitmask(@ApnType int supportedApnTypesBitmask) {
mSupportedApnTypesBitmask = supportedApnTypesBitmask;
return this;
@@ -639,7 +685,10 @@ public final class DataProfile implements Parcelable {
*
* @param protocolType The connection protocol defined in 3GPP TS 27.007 section 10.1.1.
* @return The same instance of the builder.
+ * @deprecated use {@link #setApnSetting(ApnSetting)} and
+ * {@link ApnSetting.Builder#setRoamingProtocol(int)} instead.
*/
+ @Deprecated
public @NonNull Builder setRoamingProtocolType(@ProtocolType int protocolType) {
mRoamingProtocolType = protocolType;
return this;
@@ -651,7 +700,10 @@ public final class DataProfile implements Parcelable {
* @param bearerBitmask The bearer bitmask indicating the applicable networks for this data
* profile.
* @return The same instance of the builder.
+ * @deprecated use {@link #setApnSetting(ApnSetting)} and
+ * {@link ApnSetting.Builder#setNetworkTypeBitmask(int)} instead.
*/
+ @Deprecated
public @NonNull Builder setBearerBitmask(@NetworkTypeBitMask int bearerBitmask) {
mBearerBitmask = bearerBitmask;
return this;
@@ -662,7 +714,9 @@ public final class DataProfile implements Parcelable {
*
* @param mtu The maximum transmission unit (MTU) size in bytes.
* @return The same instance of the builder.
- * @deprecated use {@link #setApnSetting(ApnSetting)} instead.
+ * @deprecated use {@link #setApnSetting(ApnSetting)} and
+ * {@link ApnSetting.Builder#setMtuV4(int)}/{@link ApnSetting.Builder#setMtuV6(int)}
+ * instead.
*/
@Deprecated
public @NonNull Builder setMtu(int mtu) {
@@ -672,11 +726,13 @@ public final class DataProfile implements Parcelable {
/**
* Set the maximum transmission unit (MTU) size in bytes, for IPv4.
- * This replaces the deprecated method setMtu.
*
* @param mtu The maximum transmission unit (MTU) size in bytes.
* @return The same instance of the builder.
+ * @deprecated Use {{@link #setApnSetting(ApnSetting)}} and
+ * {@link ApnSetting.Builder#setMtuV4(int)} instead.
*/
+ @Deprecated
public @NonNull Builder setMtuV4(int mtu) {
mMtuV4 = mtu;
return this;
@@ -687,7 +743,10 @@ public final class DataProfile implements Parcelable {
*
* @param mtu The maximum transmission unit (MTU) size in bytes.
* @return The same instance of the builder.
+ * @deprecated Use {{@link #setApnSetting(ApnSetting)}} and
+ * {@link ApnSetting.Builder#setMtuV6(int)} instead.
*/
+ @Deprecated
public @NonNull Builder setMtuV6(int mtu) {
mMtuV6 = mtu;
return this;
@@ -712,19 +771,23 @@ public final class DataProfile implements Parcelable {
* @param isPersistent {@code true} if this data profile was used to bring up the last
* default (i.e internet) data connection successfully.
* @return The same instance of the builder.
+ * @deprecated Use {{@link #setApnSetting(ApnSetting)}} and
+ * {@link ApnSetting.Builder#setPersistent(boolean)} instead.
*/
+ @Deprecated
public @NonNull Builder setPersistent(boolean isPersistent) {
mPersistent = isPersistent;
return this;
}
/**
- * Set APN setting.
+ * Set the APN setting. Note that if an APN setting is not set here, then either
+ * {@link #setApn(String)} or {@link #setTrafficDescriptor(TrafficDescriptor)} must be
+ * called. Otherwise {@link IllegalArgumentException} will be thrown when {@link #build()}
+ * the data profile.
*
- * @param apnSetting APN setting
- * @return The same instance of the builder
- *
- * @hide // TODO: Remove before T is released.
+ * @param apnSetting The APN setting.
+ * @return The same instance of the builder.
*/
public @NonNull Builder setApnSetting(@NonNull ApnSetting apnSetting) {
mApnSetting = apnSetting;
@@ -732,12 +795,13 @@ public final class DataProfile implements Parcelable {
}
/**
- * Set traffic descriptor.
- *
- * @param trafficDescriptor Traffic descriptor
- * @return The same instance of the builder
+ * Set the traffic descriptor. Note that if a traffic descriptor is not set here, then
+ * either {@link #setApnSetting(ApnSetting)} or {@link #setApn(String)} must be called.
+ * Otherwise {@link IllegalArgumentException} will be thrown when {@link #build()} the data
+ * profile.
*
- * @hide // TODO: Remove before T is released.
+ * @param trafficDescriptor The traffic descriptor.
+ * @return The same instance of the builder.
*/
public @NonNull Builder setTrafficDescriptor(@NonNull TrafficDescriptor trafficDescriptor) {
mTrafficDescriptor = trafficDescriptor;
@@ -745,9 +809,9 @@ public final class DataProfile implements Parcelable {
}
/**
- * Build the DataProfile object
+ * Build the DataProfile object.
*
- * @return The data profile object
+ * @return The data profile object.
*/
public @NonNull DataProfile build() {
if (mApnSetting == null && mApn != null) {
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 2f034752ae5f..892eb2937491 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -402,6 +402,21 @@ public abstract class DataService extends Service {
}
/**
+ * Notify the system that a given DataProfile was unthrottled.
+ *
+ * @param dataProfile DataProfile associated with an APN returned from the modem
+ */
+ public final void notifyDataProfileUnthrottled(@NonNull DataProfile dataProfile) {
+ synchronized (mApnUnthrottledCallbacks) {
+ for (IDataServiceCallback callback : mApnUnthrottledCallbacks) {
+ mHandler.obtainMessage(DATA_SERVICE_INDICATION_APN_UNTHROTTLED,
+ mSlotIndex, 0, new ApnUnthrottledIndication(dataProfile,
+ callback)).sendToTarget();
+ }
+ }
+ }
+
+ /**
* Called when the instance of data service is destroyed (e.g. got unbind or binder died)
* or when the data service provider is removed. The extended class should implement this
* method to perform cleanup works.
@@ -496,13 +511,20 @@ public abstract class DataService extends Service {
}
private static final class ApnUnthrottledIndication {
+ public final DataProfile dataProfile;
public final String apn;
public final IDataServiceCallback callback;
ApnUnthrottledIndication(String apn,
IDataServiceCallback callback) {
+ this.dataProfile = null;
this.apn = apn;
this.callback = callback;
}
+ ApnUnthrottledIndication(DataProfile dataProfile, IDataServiceCallback callback) {
+ this.dataProfile = dataProfile;
+ this.apn = null;
+ this.callback = callback;
+ }
}
private class DataServiceHandler extends Handler {
@@ -636,8 +658,13 @@ public abstract class DataService extends Service {
ApnUnthrottledIndication apnUnthrottledIndication =
(ApnUnthrottledIndication) message.obj;
try {
- apnUnthrottledIndication.callback
- .onApnUnthrottled(apnUnthrottledIndication.apn);
+ if (apnUnthrottledIndication.dataProfile != null) {
+ apnUnthrottledIndication.callback
+ .onDataProfileUnthrottled(apnUnthrottledIndication.dataProfile);
+ } else {
+ apnUnthrottledIndication.callback
+ .onApnUnthrottled(apnUnthrottledIndication.apn);
+ }
} catch (RemoteException e) {
loge("Failed to call onApnUnthrottled. " + e);
}
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index d0827159b98d..051d6c5d5ec0 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -260,8 +260,8 @@ public class DataServiceCallback {
/**
* Unthrottles the APN on the current transport. There is no matching "APN throttle" method.
- * Instead, the APN is throttled for the time specified in
- * {@link DataCallResponse#getRetryDurationMillis}.
+ * Instead, the APN is throttled when {@link IDataService#setupDataCall} fails within
+ * the time specified by {@link DataCallResponse#getRetryDurationMillis}.
* <p/>
* see: {@link DataCallResponse#getRetryDurationMillis}
*
@@ -279,4 +279,27 @@ public class DataServiceCallback {
Rlog.e(TAG, "onApnUnthrottled: callback is null!");
}
}
+
+ /**
+ * Unthrottles the DataProfile on the current transport.
+ * There is no matching "DataProfile throttle" method.
+ * Instead, the DataProfile is throttled when {@link IDataService#setupDataCall} fails within
+ * the time specified by {@link DataCallResponse#getRetryDurationMillis}.
+ * <p/>
+ * see: {@link DataCallResponse#getRetryDurationMillis}
+ *
+ * @param dataProfile DataProfile containing the APN to be throttled
+ */
+ public void onDataProfileUnthrottled(final @NonNull DataProfile dataProfile) {
+ if (mCallback != null) {
+ try {
+ if (DBG) Rlog.d(TAG, "onDataProfileUnthrottled");
+ mCallback.onDataProfileUnthrottled(dataProfile);
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "onDataProfileUnthrottled: remote exception", e);
+ }
+ } else {
+ Rlog.e(TAG, "onDataProfileUnthrottled: callback is null!");
+ }
+ }
}
diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
index 9cc2feac331a..8205b5eb4d37 100644
--- a/telephony/java/android/telephony/data/IDataServiceCallback.aidl
+++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
@@ -17,6 +17,7 @@
package android.telephony.data;
import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataProfile;
/**
* The call back interface
@@ -33,4 +34,5 @@ oneway interface IDataServiceCallback
void onHandoverStarted(int result);
void onHandoverCancelled(int result);
void onApnUnthrottled(in String apn);
+ void onDataProfileUnthrottled(in DataProfile dp);
}
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index ab35d77c0b4d..4efd159a289b 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -19,8 +19,10 @@ import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.SystemApi;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.RemoteException;
import android.service.euicc.EuiccProfileInfo;
@@ -61,19 +63,21 @@ import java.util.concurrent.Executor;
* @hide
*/
@SystemApi
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
public class EuiccCardManager {
private static final String TAG = "EuiccCardManager";
/** Reason for canceling a profile download session */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "CANCEL_REASON_" }, value = {
+ @IntDef(prefix = {"CANCEL_REASON_"}, value = {
CANCEL_REASON_END_USER_REJECTED,
CANCEL_REASON_POSTPONED,
CANCEL_REASON_TIMEOUT,
CANCEL_REASON_PPR_NOT_ALLOWED
})
/** @hide */
- public @interface CancelReason {}
+ public @interface CancelReason {
+ }
/**
* The end user has rejected the download. The profile will be put into the error state and
@@ -96,13 +100,14 @@ public class EuiccCardManager {
/** Options for resetting eUICC memory */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, prefix = { "RESET_OPTION_" }, value = {
+ @IntDef(flag = true, prefix = {"RESET_OPTION_"}, value = {
RESET_OPTION_DELETE_OPERATIONAL_PROFILES,
RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES,
RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS
})
/** @hide */
- public @interface ResetOption {}
+ public @interface ResetOption {
+ }
/** Deletes all operational profiles. */
public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1;
@@ -124,6 +129,10 @@ public class EuiccCardManager {
/** Result code indicating the caller is not the active LPA. */
public static final int RESULT_CALLER_NOT_ALLOWED = -3;
+
+ /** Result code when the requested profile is not found */
+ public static final int RESULT_PROFILE_NOT_FOUND = -4;
+
/**
* Callback to receive the result of an eUICC card API.
*
@@ -134,9 +143,9 @@ public class EuiccCardManager {
* This method will be called when an eUICC card API call is completed.
*
* @param resultCode This can be {@link #RESULT_OK} or other positive values returned by the
- * eUICC.
- * @param result The result object. It can be null if the {@code resultCode} is not
- * {@link #RESULT_OK}.
+ * eUICC.
+ * @param result The result object. It can be null if the {@code resultCode} is not
+ * {@link #RESULT_OK}.
*/
void onComplete(int resultCode, T result);
}
@@ -159,7 +168,7 @@ public class EuiccCardManager {
/**
* Requests all the profiles on eUicc.
*
- * @param cardId The Id of the eUICC.
+ * @param cardId The Id of the eUICC.
* @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and all the profiles.
*/
@@ -187,8 +196,8 @@ public class EuiccCardManager {
/**
* Requests the profile of the given iccid.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
* @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and profile.
*/
@@ -214,16 +223,47 @@ public class EuiccCardManager {
}
/**
+ * Requests the enabled profile for a given port on an eUicc.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param portIndex The portIndex to use. The port may be active or inactive. As long as the
+ * ICCID is known, an APDU will be sent through to read the enabled profile.
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback to get the result code and the profile.
+ */
+ public void requestEnabledProfileForPort(@NonNull String cardId, int portIndex,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull ResultCallback<EuiccProfileInfo> callback) {
+ try {
+ getIEuiccCardController().getEnabledProfile(mContext.getOpPackageName(), cardId,
+ portIndex,
+ new IGetProfileCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccProfileInfo profile) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onComplete(resultCode, profile));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling requestEnabledProfileForPort", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Disables the profile of the given iccid.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile.
- * @param refresh Whether sending the REFRESH command to modem.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
+ * @param refresh Whether sending the REFRESH command to modem.
* @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code.
- *
* @deprecated instead use {@link #disableProfile(String, String, int, boolean, Executor,
- * ResultCallback)}
+ * ResultCallback)}
*/
@Deprecated
public void disableProfile(String cardId, String iccid, boolean refresh,
@@ -247,15 +287,16 @@ public class EuiccCardManager {
throw e.rethrowFromSystemServer();
}
}
+
/**
* Disables the profile of the given ICCID.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
* @param portIndex the Port index is the unique index referring to a port.
- * @param refresh Whether sending the REFRESH command to modem.
- * @param executor The executor through which the callback should be invoked.
- * @param callback The callback to get the result code.
+ * @param refresh Whether sending the REFRESH command to modem.
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback to get the result code.
*/
public void disableProfile(@Nullable String cardId, @Nullable String iccid, int portIndex,
boolean refresh, @NonNull @CallbackExecutor Executor executor,
@@ -283,14 +324,13 @@ public class EuiccCardManager {
* Switches from the current profile to another profile. The current profile will be disabled
* and the specified profile will be enabled.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile to switch to.
- * @param refresh Whether sending the REFRESH command to modem.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile to switch to.
+ * @param refresh Whether sending the REFRESH command to modem.
* @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and the EuiccProfileInfo enabled.
- *
* @deprecated instead use {@link #switchToProfile(String, String, int, boolean, Executor,
- * ResultCallback)}
+ * ResultCallback)}
*/
@Deprecated
public void switchToProfile(String cardId, String iccid, boolean refresh,
@@ -320,12 +360,12 @@ public class EuiccCardManager {
* and the specified profile will be enabled. Here portIndex specifies on which port the
* profile is to be enabled.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile to switch to.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile to switch to.
* @param portIndex The Port index is the unique index referring to a port.
- * @param refresh Whether sending the REFRESH command to modem.
- * @param executor The executor through which the callback should be invoked.
- * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+ * @param refresh Whether sending the REFRESH command to modem.
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
*/
public void switchToProfile(@Nullable String cardId, @Nullable String iccid, int portIndex,
boolean refresh, @NonNull @CallbackExecutor Executor executor,
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index aa514b99dad3..389cc6dba3bf 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -20,6 +20,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
@@ -58,6 +59,7 @@ import java.util.stream.Collectors;
*
* <p>See {@link #isEnabled} before attempting to use these APIs.
*/
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
public class EuiccManager {
/**
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 683bb92845ba..bce210fa22a0 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -21,11 +21,13 @@ import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -61,6 +63,7 @@ import java.util.function.Consumer;
* Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an instance of this
* manager.
*/
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public class ImsMmTelManager implements RegistrationManager {
private static final String TAG = "ImsMmTelManager";
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 1b047c77d80b..34158685b6d1 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -19,11 +19,13 @@ package android.telephony.ims;
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -53,6 +55,7 @@ import java.util.function.Consumer;
*
* Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this manager.
*/
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public class ImsRcsManager {
private static final String TAG = "ImsRcsManager";
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 9ab5aeb9c34c..53dff545b0ce 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -17,6 +17,7 @@
package android.telephony.ims;
import android.annotation.LongDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -44,11 +45,18 @@ import android.util.SparseArray;
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
/**
* Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
@@ -173,7 +181,21 @@ public class ImsService extends Service {
private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
private IImsServiceControllerListener mListener;
+ private Executor mExecutor;
+ /**
+ * Create a new ImsService.
+ * <p>
+ * Method stubs called from the framework will be called asynchronously. Vendor specifies the
+ * {@link Executor} that the methods stubs will be called. If mExecutor is set to null by
+ * vendor use Runnable::run.
+ */
+ public ImsService() {
+ mExecutor = ImsService.this.getExecutor();
+ if (mExecutor == null) {
+ mExecutor = Runnable::run;
+ }
+ }
/**
* Listener that notifies the framework of ImsService changes.
@@ -201,78 +223,132 @@ public class ImsService extends Service {
@Override
public IImsMmTelFeature createMmTelFeature(int slotId) {
- return createMmTelFeatureInternal(slotId);
+ return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId),
+ "createMmTelFeature");
}
@Override
public IImsRcsFeature createRcsFeature(int slotId) {
- return createRcsFeatureInternal(slotId);
+ return executeMethodAsyncForResult(() -> createRcsFeatureInternal(slotId),
+ "createRcsFeature");
}
@Override
public void addFeatureStatusCallback(int slotId, int featureType,
IImsFeatureStatusCallback c) {
- ImsService.this.addImsFeatureStatusCallback(slotId, featureType, c);
+ executeMethodAsync(() -> ImsService.this.addImsFeatureStatusCallback(
+ slotId, featureType, c), "addFeatureStatusCallback");
}
@Override
public void removeFeatureStatusCallback(int slotId, int featureType,
IImsFeatureStatusCallback c) {
- ImsService.this.removeImsFeatureStatusCallback(slotId, featureType, c);
+ executeMethodAsync(() -> ImsService.this.removeImsFeatureStatusCallback(
+ slotId, featureType, c), "removeFeatureStatusCallback");
}
@Override
public void removeImsFeature(int slotId, int featureType) {
- ImsService.this.removeImsFeature(slotId, featureType);
+ executeMethodAsync(() -> ImsService.this.removeImsFeature(slotId, featureType),
+ "removeImsFeature");
}
@Override
public ImsFeatureConfiguration querySupportedImsFeatures() {
- return ImsService.this.querySupportedImsFeatures();
+ return executeMethodAsyncForResult(() -> ImsService.this.querySupportedImsFeatures(),
+ "ImsFeatureConfiguration");
}
@Override
public long getImsServiceCapabilities() {
- long caps = ImsService.this.getImsServiceCapabilities();
- long sanitizedCaps = sanitizeCapabilities(caps);
- if (caps != sanitizedCaps) {
- Log.w(LOG_TAG, "removing invalid bits from field: 0x"
- + Long.toHexString(caps ^ sanitizedCaps));
- }
- return sanitizedCaps;
+ return executeMethodAsyncForResult(() -> {
+ long caps = ImsService.this.getImsServiceCapabilities();
+ long sanitizedCaps = sanitizeCapabilities(caps);
+ if (caps != sanitizedCaps) {
+ Log.w(LOG_TAG, "removing invalid bits from field: 0x"
+ + Long.toHexString(caps ^ sanitizedCaps));
+ }
+ return sanitizedCaps;
+ }, "getImsServiceCapabilities");
}
@Override
public void notifyImsServiceReadyForFeatureCreation() {
- ImsService.this.readyForFeatureCreation();
+ executeMethodAsync(() -> ImsService.this.readyForFeatureCreation(),
+ "notifyImsServiceReadyForFeatureCreation");
}
@Override
public IImsConfig getConfig(int slotId) {
- ImsConfigImplBase c = ImsService.this.getConfig(slotId);
- return c != null ? c.getIImsConfig() : null;
+ return executeMethodAsyncForResult(() -> {
+ ImsConfigImplBase c = ImsService.this.getConfig(slotId);
+ if (c != null) {
+ c.setDefaultExecutor(mExecutor);
+ return c.getIImsConfig();
+ } else {
+ return null;
+ }
+ }, "getConfig");
}
@Override
public IImsRegistration getRegistration(int slotId) {
- ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
- return r != null ? r.getBinder() : null;
+ return executeMethodAsyncForResult(() -> {
+ ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
+ if (r != null) {
+ r.setDefaultExecutor(mExecutor);
+ return r.getBinder();
+ } else {
+ return null;
+ }
+ }, "getRegistration");
}
@Override
public ISipTransport getSipTransport(int slotId) {
- SipTransportImplBase s = ImsService.this.getSipTransport(slotId);
- return s != null ? s.getBinder() : null;
+ return executeMethodAsyncForResult(() -> {
+ SipTransportImplBase s = ImsService.this.getSipTransport(slotId);
+ if (s != null) {
+ s.setDefaultExecutor(mExecutor);
+ return s.getBinder();
+ } else {
+ return null;
+ }
+ }, "getSipTransport");
}
@Override
public void enableIms(int slotId) {
- ImsService.this.enableIms(slotId);
+ executeMethodAsync(() -> ImsService.this.enableIms(slotId), "enableIms");
}
@Override
public void disableIms(int slotId) {
- ImsService.this.disableIms(slotId);
+ executeMethodAsync(() -> ImsService.this.disableIms(slotId), "disableIms");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ return null;
+ }
}
};
@@ -300,6 +376,7 @@ public class ImsService extends Service {
MmTelFeature f = createMmTelFeature(slotId);
if (f != null) {
setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
+ f.setDefaultExecutor(mExecutor);
return f.getBinder();
} else {
Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
@@ -310,6 +387,7 @@ public class ImsService extends Service {
private IImsRcsFeature createRcsFeatureInternal(int slotId) {
RcsFeature f = createRcsFeature(slotId);
if (f != null) {
+ f.setDefaultExecutor(mExecutor);
setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
return f.getBinder();
} else {
@@ -562,4 +640,15 @@ public class ImsService extends Service {
result.append("}");
return result.toString();
}
+
+ /**
+ * The ImsService will now be able to define an Executor that the ImsService can be used to
+ * execute the methods. By default all ImsService level method calls will use this Executor.
+ * The ImsService has set the default executor as Runnable::run,
+ * Should be override or default executor will be used.
+ * @return an Executor used to execute methods called remotely by the framework.
+ */
+ public @NonNull Executor getExecutor() {
+ return Runnable::run;
+ }
}
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index abc5606e6743..dbf4c99939de 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.StringDef;
@@ -62,6 +63,7 @@ import java.util.concurrent.Executor;
* @hide
*/
@SystemApi
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public class ProvisioningManager {
/**@hide*/
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index a2015cd8f22c..090d4136872e 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -21,7 +21,9 @@ import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -42,6 +44,7 @@ import java.util.function.Consumer;
/**
* Manages IMS Service registration state for associated {@link ImsFeature}s.
*/
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public interface RegistrationManager {
/**
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index f913df588f40..94e9afbe9e38 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -21,6 +21,7 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
@@ -57,6 +58,7 @@ import java.util.concurrent.Executor;
* @hide
*/
@SystemApi
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
public class SipDelegateManager {
/**
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 9a3f592480d1..7fdf21b3e5ff 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -40,16 +40,25 @@ import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
import android.util.ArraySet;
+import android.util.Log;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsUt;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
/**
* Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
@@ -60,6 +69,7 @@ import java.util.Set;
public class MmTelFeature extends ImsFeature {
private static final String LOG_TAG = "MmTelFeature";
+ private Executor mExecutor;
/**
* @hide
@@ -68,160 +78,261 @@ public class MmTelFeature extends ImsFeature {
public MmTelFeature() {
}
+ /**
+ * Create a new MmTelFeature using the Executor specified for methods being called by the
+ * framework.
+ * @param executor The executor for the framework to use when executing the methods overridden
+ * by the implementation of MmTelFeature.
+ * @hide
+ */
+ @SystemApi
+ public MmTelFeature(@NonNull Executor executor) {
+ super();
+ mExecutor = executor;
+ }
+
private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
@Override
public void setListener(IImsMmTelListener l) {
- MmTelFeature.this.setListener(l);
+ executeMethodAsyncNoException(() -> MmTelFeature.this.setListener(l), "setListener");
}
@Override
public int getFeatureState() throws RemoteException {
- try {
- return MmTelFeature.this.getFeatureState();
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
+ return executeMethodAsyncForResult(() -> MmTelFeature.this.getFeatureState(),
+ "getFeatureState");
}
-
@Override
public ImsCallProfile createCallProfile(int callSessionType, int callType)
throws RemoteException {
- synchronized (mLock) {
- try {
- return MmTelFeature.this.createCallProfile(callSessionType, callType);
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
- }
+ return executeMethodAsyncForResult(() -> MmTelFeature.this.createCallProfile(
+ callSessionType, callType), "createCallProfile");
}
@Override
public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types)
throws RemoteException {
- synchronized (mLock) {
- try {
- MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(new ArraySet<>(types));
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
- }
+ executeMethodAsync(() -> MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(
+ new ArraySet<>(types)), "changeOfferedRtpHeaderExtensionTypes");
}
@Override
public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
- synchronized (mLock) {
- return createCallSessionInterface(profile);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ IImsCallSession result = executeMethodAsyncForResult(() -> {
+ try {
+ return createCallSessionInterface(profile);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return null;
+ }
+ }, "createCallSession");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
}
+
+ return result;
}
@Override
public int shouldProcessCall(String[] numbers) {
- synchronized (mLock) {
- return MmTelFeature.this.shouldProcessCall(numbers);
+ Integer result = executeMethodAsyncForResultNoException(() ->
+ MmTelFeature.this.shouldProcessCall(numbers), "shouldProcessCall");
+ if (result != null) {
+ return result.intValue();
+ } else {
+ return PROCESS_CALL_CSFB;
}
}
@Override
public IImsUt getUtInterface() throws RemoteException {
- synchronized (mLock) {
- return MmTelFeature.this.getUtInterface();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ IImsUt result = executeMethodAsyncForResult(() -> {
+ try {
+ return MmTelFeature.this.getUtInterface();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return null;
+ }
+ }, "getUtInterface");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
}
+
+ return result;
}
@Override
public IImsEcbm getEcbmInterface() throws RemoteException {
- synchronized (mLock) {
- return MmTelFeature.this.getEcbmInterface();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ IImsEcbm result = executeMethodAsyncForResult(() -> {
+ try {
+ return MmTelFeature.this.getEcbmInterface();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return null;
+ }
+ }, "getEcbmInterface");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
}
+
+ return result;
}
@Override
public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
- synchronized (mLock) {
- try {
- MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
- }
+ executeMethodAsync(() -> MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage),
+ "setUiTtyMode");
}
@Override
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
- synchronized (mLock) {
- return MmTelFeature.this.getMultiEndpointInterface();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ IImsMultiEndpoint result = executeMethodAsyncForResult(() -> {
+ try {
+ return MmTelFeature.this.getMultiEndpointInterface();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return null;
+ }
+ }, "getMultiEndpointInterface");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
}
+
+ return result;
}
@Override
public int queryCapabilityStatus() {
- return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+ Integer result = executeMethodAsyncForResultNoException(() -> MmTelFeature.this
+ .queryCapabilityStatus().mCapabilities, "queryCapabilityStatus");
+
+ if (result != null) {
+ return result.intValue();
+ } else {
+ return 0;
+ }
}
@Override
public void addCapabilityCallback(IImsCapabilityCallback c) {
- // no need to lock, structure already handles multithreading.
- MmTelFeature.this.addCapabilityCallback(c);
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .addCapabilityCallback(c), "addCapabilityCallback");
}
@Override
public void removeCapabilityCallback(IImsCapabilityCallback c) {
- // no need to lock, structure already handles multithreading.
- MmTelFeature.this.removeCapabilityCallback(c);
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .removeCapabilityCallback(c), "removeCapabilityCallback");
}
@Override
public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
IImsCapabilityCallback c) {
- MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .requestChangeEnabledCapabilities(request, c),
+ "changeCapabilitiesConfiguration");
}
@Override
public void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c) {
- queryCapabilityConfigurationInternal(capability, radioTech, c);
+ executeMethodAsyncNoException(() -> queryCapabilityConfigurationInternal(
+ capability, radioTech, c), "queryCapabilityConfiguration");
}
@Override
public void setSmsListener(IImsSmsListener l) {
- MmTelFeature.this.setSmsListener(l);
+ executeMethodAsyncNoException(() -> MmTelFeature.this.setSmsListener(l),
+ "setSmsListener");
}
@Override
public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
byte[] pdu) {
- synchronized (mLock) {
- MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
- }
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .sendSms(token, messageRef, format, smsc, retry, pdu), "sendSms");
}
@Override
public void acknowledgeSms(int token, int messageRef, int result) {
- synchronized (mLock) {
- MmTelFeature.this.acknowledgeSms(token, messageRef, result);
- }
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .acknowledgeSms(token, messageRef, result), "acknowledgeSms");
}
@Override
public void acknowledgeSmsReport(int token, int messageRef, int result) {
- synchronized (mLock) {
- MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
- }
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .acknowledgeSmsReport(token, messageRef, result), "acknowledgeSmsReport");
}
@Override
public String getSmsFormat() {
- synchronized (mLock) {
- return MmTelFeature.this.getSmsFormat();
- }
+ return executeMethodAsyncForResultNoException(() -> MmTelFeature.this
+ .getSmsFormat(), "getSmsFormat");
}
@Override
public void onSmsReady() {
- synchronized (mLock) {
- MmTelFeature.this.onSmsReady();
+ executeMethodAsyncNoException(() -> MmTelFeature.this.onSmsReady(),
+ "onSmsReady");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private void executeMethodAsyncNoException(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResultNoException(Supplier<T> r,
+ String errorLogName) {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ return null;
}
}
};
@@ -672,7 +783,12 @@ public class MmTelFeature extends ImsFeature {
public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
throws RemoteException {
ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
- return s != null ? s.getServiceImpl() : null;
+ if (s != null) {
+ s.setDefaultExecutor(mExecutor);
+ return s.getServiceImpl();
+ } else {
+ return null;
+ }
}
/**
@@ -713,7 +829,12 @@ public class MmTelFeature extends ImsFeature {
*/
protected IImsUt getUtInterface() throws RemoteException {
ImsUtImplBase utImpl = getUt();
- return utImpl != null ? utImpl.getInterface() : null;
+ if (utImpl != null) {
+ utImpl.setDefaultExecutor(mExecutor);
+ return utImpl.getInterface();
+ } else {
+ return null;
+ }
}
/**
@@ -721,7 +842,12 @@ public class MmTelFeature extends ImsFeature {
*/
protected IImsEcbm getEcbmInterface() throws RemoteException {
ImsEcbmImplBase ecbmImpl = getEcbm();
- return ecbmImpl != null ? ecbmImpl.getImsEcbm() : null;
+ if (ecbmImpl != null) {
+ ecbmImpl.setDefaultExecutor(mExecutor);
+ return ecbmImpl.getImsEcbm();
+ } else {
+ return null;
+ }
}
/**
@@ -729,7 +855,12 @@ public class MmTelFeature extends ImsFeature {
*/
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
- return multiendpointImpl != null ? multiendpointImpl.getIImsMultiEndpoint() : null;
+ if (multiendpointImpl != null) {
+ multiendpointImpl.setDefaultExecutor(mExecutor);
+ return multiendpointImpl.getIImsMultiEndpoint();
+ } else {
+ return null;
+ }
}
/**
@@ -859,4 +990,16 @@ public class MmTelFeature extends ImsFeature {
public final IImsMmTelFeature getBinder() {
return mImsMMTelBinder;
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of MmTelFeature.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mExecutor == null) {
+ mExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 18cc37d7fbda..11cf0e3f7855 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -70,7 +70,7 @@ public class RcsFeature extends ImsFeature {
// Reference the outer class in order to have better test coverage metrics instead of
// creating a inner class referencing the outer class directly.
private final RcsFeature mReference;
- private final Executor mExecutor;
+ private Executor mExecutor;
RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) {
mReference = classRef;
@@ -259,7 +259,7 @@ public class RcsFeature extends ImsFeature {
}
}
- private final Executor mExecutor;
+ private Executor mExecutor;
private final RcsFeatureBinder mImsRcsBinder;
private RcsCapabilityExchangeImplBase mCapabilityExchangeImpl;
private CapabilityExchangeEventListener mCapExchangeEventListener;
@@ -270,13 +270,9 @@ public class RcsFeature extends ImsFeature {
* Method stubs called from the framework will be called asynchronously. To specify the
* {@link Executor} that the methods stubs will be called, use
* {@link RcsFeature#RcsFeature(Executor)} instead.
- *
- * @deprecated Use {@link #RcsFeature(Executor)} to create the RcsFeature.
*/
- @Deprecated
public RcsFeature() {
super();
- mExecutor = Runnable::run;
// Run on the Binder threads that call them.
mImsRcsBinder = new RcsFeatureBinder(this, mExecutor);
}
@@ -477,4 +473,17 @@ public class RcsFeature extends ImsFeature {
return mCapabilityExchangeImpl;
}
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of RcsFeature.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mImsRcsBinder.mExecutor == null) {
+ mExecutor = executor;
+ mImsRcsBinder.mExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index 9293a40d9a33..7a1a2af060d2 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -93,8 +93,8 @@ public interface CapabilityExchangeEventListener {
* Notify the framework that the ImsService has refreshed the PUBLISH
* internally, which has resulted in a new PUBLISH result.
* <p>
- * This method must return both SUCCESS (200 OK) and FAILURE (300+) codes in order to
- * keep the AOSP stack up to date.
+ * This method must be called to notify the framework of SUCCESS (200 OK) and FAILURE (300+)
+ * codes in order to keep the AOSP stack up to date.
* @param reasonCode The SIP response code sent from the network.
* @param reasonPhrase The optional reason response from the network. If the
* network provided no reason with the sip code, the string should be empty.
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index a3a6cb864fa5..e8100957517f 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -30,12 +30,20 @@ import android.telephony.ims.RtpHeaderExtension;
import android.telephony.ims.RtpHeaderExtensionType;
import android.telephony.ims.aidl.IImsCallSessionListener;
import android.util.ArraySet;
+import android.util.Log;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsVideoCallProvider;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
/**
* Base implementation of IImsCallSession, which implements stub versions of the methods available.
@@ -48,6 +56,8 @@ import java.util.Set;
// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
// will break other implementations of ImsCallSession maintained by other ImsServices.
public class ImsCallSessionImplBase implements AutoCloseable {
+
+ private static final String LOG_TAG = "ImsCallSessionImplBase";
/**
* Notify USSD Mode.
*/
@@ -110,185 +120,235 @@ public class ImsCallSessionImplBase implements AutoCloseable {
}
}
+ private Executor mExecutor = Runnable::run;
+
// Non-final for injection by tests
private IImsCallSession mServiceImpl = new IImsCallSession.Stub() {
@Override
public void close() {
- ImsCallSessionImplBase.this.close();
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.close(), "close");
}
@Override
public String getCallId() {
- return ImsCallSessionImplBase.this.getCallId();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallId(),
+ "getCallId");
}
@Override
public ImsCallProfile getCallProfile() {
- return ImsCallSessionImplBase.this.getCallProfile();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallProfile(),
+ "getCallProfile");
}
@Override
public ImsCallProfile getLocalCallProfile() {
- return ImsCallSessionImplBase.this.getLocalCallProfile();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
+ .getLocalCallProfile(), "getLocalCallProfile");
}
@Override
public ImsCallProfile getRemoteCallProfile() {
- return ImsCallSessionImplBase.this.getRemoteCallProfile();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
+ .getRemoteCallProfile(), "getRemoteCallProfile");
}
@Override
public String getProperty(String name) {
- return ImsCallSessionImplBase.this.getProperty(name);
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getProperty(name),
+ "getProperty");
}
@Override
public int getState() {
- return ImsCallSessionImplBase.this.getState();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getState(),
+ "getState");
}
@Override
public boolean isInCall() {
- return ImsCallSessionImplBase.this.isInCall();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isInCall(),
+ "isInCall");
}
@Override
public void setListener(IImsCallSessionListener listener) {
- ImsCallSessionImplBase.this.setListener(new ImsCallSessionListener(listener));
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.setListener(
+ new ImsCallSessionListener(listener)), "setListener");
}
@Override
public void setMute(boolean muted) {
- ImsCallSessionImplBase.this.setMute(muted);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.setMute(muted), "setMute");
}
@Override
public void start(String callee, ImsCallProfile profile) {
- ImsCallSessionImplBase.this.start(callee, profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.start(callee, profile), "start");
}
@Override
public void startConference(String[] participants, ImsCallProfile profile) throws
RemoteException {
- ImsCallSessionImplBase.this.startConference(participants, profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.startConference(participants,
+ profile), "startConference");
}
@Override
public void accept(int callType, ImsStreamMediaProfile profile) {
- ImsCallSessionImplBase.this.accept(callType, profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.accept(callType, profile),
+ "accept");
}
@Override
public void deflect(String deflectNumber) {
- ImsCallSessionImplBase.this.deflect(deflectNumber);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.deflect(deflectNumber),
+ "deflect");
}
@Override
public void reject(int reason) {
- ImsCallSessionImplBase.this.reject(reason);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.reject(reason), "reject");
}
@Override
public void transfer(@NonNull String number, boolean isConfirmationRequired) {
- ImsCallSessionImplBase.this.transfer(number, isConfirmationRequired);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.transfer(number,
+ isConfirmationRequired), "transfer");
}
@Override
public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
- ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
- otherSession.setServiceImpl(transferToSession);
- ImsCallSessionImplBase.this.transfer(otherSession);
+ executeMethodAsync(() -> {
+ ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
+ otherSession.setServiceImpl(transferToSession);
+ ImsCallSessionImplBase.this.transfer(otherSession);
+ }, "consultativeTransfer");
}
@Override
public void terminate(int reason) {
- ImsCallSessionImplBase.this.terminate(reason);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.terminate(reason), "terminate");
}
@Override
public void hold(ImsStreamMediaProfile profile) {
- ImsCallSessionImplBase.this.hold(profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.hold(profile), "hold");
}
@Override
public void resume(ImsStreamMediaProfile profile) {
- ImsCallSessionImplBase.this.resume(profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.resume(profile), "resume");
}
@Override
public void merge() {
- ImsCallSessionImplBase.this.merge();
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.merge(), "merge");
}
@Override
public void update(int callType, ImsStreamMediaProfile profile) {
- ImsCallSessionImplBase.this.update(callType, profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.update(callType, profile),
+ "update");
}
@Override
public void extendToConference(String[] participants) {
- ImsCallSessionImplBase.this.extendToConference(participants);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.extendToConference(participants),
+ "extendToConference");
}
@Override
public void inviteParticipants(String[] participants) {
- ImsCallSessionImplBase.this.inviteParticipants(participants);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.inviteParticipants(participants),
+ "inviteParticipants");
}
@Override
public void removeParticipants(String[] participants) {
- ImsCallSessionImplBase.this.removeParticipants(participants);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.removeParticipants(participants),
+ "removeParticipants");
}
@Override
public void sendDtmf(char c, Message result) {
- ImsCallSessionImplBase.this.sendDtmf(c, result);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendDtmf(c, result), "sendDtmf");
}
@Override
public void startDtmf(char c) {
- ImsCallSessionImplBase.this.startDtmf(c);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.startDtmf(c), "startDtmf");
}
@Override
public void stopDtmf() {
- ImsCallSessionImplBase.this.stopDtmf();
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.stopDtmf(), "stopDtmf");
}
@Override
public void sendUssd(String ussdMessage) {
- ImsCallSessionImplBase.this.sendUssd(ussdMessage);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendUssd(ussdMessage), "sendUssd");
}
@Override
public IImsVideoCallProvider getVideoCallProvider() {
- return ImsCallSessionImplBase.this.getVideoCallProvider();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
+ .getVideoCallProvider(), "getVideoCallProvider");
}
@Override
public boolean isMultiparty() {
- return ImsCallSessionImplBase.this.isMultiparty();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isMultiparty(),
+ "isMultiparty");
}
@Override
public void sendRttModifyRequest(ImsCallProfile toProfile) {
- ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile),
+ "sendRttModifyRequest");
}
@Override
public void sendRttModifyResponse(boolean status) {
- ImsCallSessionImplBase.this.sendRttModifyResponse(status);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyResponse(status),
+ "sendRttModifyResponse");
}
@Override
public void sendRttMessage(String rttMessage) {
- ImsCallSessionImplBase.this.sendRttMessage(rttMessage);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttMessage(rttMessage),
+ "sendRttMessage");
}
@Override
public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> extensions) {
- ImsCallSessionImplBase.this.sendRtpHeaderExtensions(
- new ArraySet<RtpHeaderExtension>(extensions));
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRtpHeaderExtensions(
+ new ArraySet<RtpHeaderExtension>(extensions)), "sendRtpHeaderExtensions");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "ImsCallSessionImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "ImsCallSessionImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ return null;
+ }
}
};
@@ -674,4 +734,14 @@ public class ImsCallSessionImplBase implements AutoCloseable {
public void setServiceImpl(IImsCallSession serviceImpl) {
mServiceImpl = serviceImpl;
}
+
+ /**
+ * Set default Executor from MmTelFeature.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsCallSession.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index d75da9035124..11fc328a42c7 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -33,12 +33,21 @@ import android.util.Log;
import com.android.ims.ImsConfig;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.RemoteCallbackListExt;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
/**
* Controls the modification of IMS specific configurations. For more information on the supported
@@ -81,21 +90,48 @@ public class ImsConfigImplBase {
WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
+ private final Object mLock = new Object();
+ private Executor mExecutor;
@VisibleForTesting
- public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) {
+ public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Executor executor) {
+ mExecutor = executor;
mImsConfigImplBaseWeakReference =
new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
}
@Override
public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
- getImsConfigImpl().addImsConfigCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().addImsConfigCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "addImsConfigCallback");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception addImsConfigCallback");
+ throw exceptionRef.get();
+ }
}
@Override
public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
- getImsConfigImpl().removeImsConfigCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().removeImsConfigCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "removeImsConfigCallback");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception removeImsConfigCallback");
+ throw exceptionRef.get();
+ }
}
/**
@@ -108,16 +144,34 @@ public class ImsConfigImplBase {
* unavailable.
*/
@Override
- public synchronized int getConfigInt(int item) throws RemoteException {
- if (mProvisionedIntValue.containsKey(item)) {
- return mProvisionedIntValue.get(item);
- } else {
- int retVal = getImsConfigImpl().getConfigInt(item);
- if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
- updateCachedValue(item, retVal, false);
+ public int getConfigInt(int item) throws RemoteException {
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ int retVal = executeMethodAsyncForResult(()-> {
+ int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
+ synchronized (mLock) {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedIntValue.get(item);
+ } else {
+ try {
+ returnVal = getImsConfigImpl().getConfigInt(item);
+ if (returnVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
+ mProvisionedIntValue.put(item, returnVal);
+ }
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return returnVal;
+ }
+ }
}
- return retVal;
+ return returnVal;
+ }, "getConfigInt");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception getConfigString");
+ throw exceptionRef.get();
}
+
+ return retVal;
}
/**
@@ -129,16 +183,34 @@ public class ImsConfigImplBase {
* @return value in String format.
*/
@Override
- public synchronized String getConfigString(int item) throws RemoteException {
- if (mProvisionedStringValue.containsKey(item)) {
- return mProvisionedStringValue.get(item);
- } else {
- String retVal = getImsConfigImpl().getConfigString(item);
- if (retVal != null) {
- updateCachedValue(item, retVal, false);
+ public String getConfigString(int item) throws RemoteException {
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ String retVal = executeMethodAsyncForResult(()-> {
+ String returnVal = null;
+ synchronized (mLock) {
+ if (mProvisionedStringValue.containsKey(item)) {
+ returnVal = mProvisionedStringValue.get(item);
+ } else {
+ try {
+ returnVal = getImsConfigImpl().getConfigString(item);
+ if (returnVal != null) {
+ mProvisionedStringValue.put(item, returnVal);
+ }
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return returnVal;
+ }
+ }
}
- return retVal;
+ return returnVal;
+ }, "getConfigString");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception getConfigString");
+ throw exceptionRef.get();
}
+
+ return retVal;
}
/**
@@ -153,14 +225,32 @@ public class ImsConfigImplBase {
* {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
*/
@Override
- public synchronized int setConfigInt(int item, int value) throws RemoteException {
- mProvisionedIntValue.remove(item);
- int retVal = getImsConfigImpl().setConfig(item, value);
- if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
- updateCachedValue(item, value, true);
- } else {
- Log.d(TAG, "Set provision value of " + item +
- " to " + value + " failed with error code " + retVal);
+ public int setConfigInt(int item, int value) throws RemoteException {
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ int retVal = executeMethodAsyncForResult(()-> {
+ int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
+ try {
+ synchronized (mLock) {
+ mProvisionedIntValue.remove(item);
+ returnVal = getImsConfigImpl().setConfig(item, value);
+ if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ mProvisionedIntValue.put(item, value);
+ } else {
+ Log.d(TAG, "Set provision value of " + item
+ + " to " + value + " failed with error code " + returnVal);
+ }
+ }
+ notifyImsConfigChanged(item, value);
+ return returnVal;
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return returnVal;
+ }
+ }, "setConfigInt");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception setConfigInt");
+ throw exceptionRef.get();
}
return retVal;
@@ -178,12 +268,30 @@ public class ImsConfigImplBase {
* {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
*/
@Override
- public synchronized int setConfigString(int item, String value)
+ public int setConfigString(int item, String value)
throws RemoteException {
- mProvisionedStringValue.remove(item);
- int retVal = getImsConfigImpl().setConfig(item, value);
- if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
- updateCachedValue(item, value, true);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ int retVal = executeMethodAsyncForResult(()-> {
+ int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
+ try {
+ synchronized (mLock) {
+ mProvisionedStringValue.remove(item);
+ returnVal = getImsConfigImpl().setConfig(item, value);
+ if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ mProvisionedStringValue.put(item, value);
+ }
+ }
+ notifyImsConfigChanged(item, value);
+ return returnVal;
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return returnVal;
+ }
+ }, "setConfigString");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception setConfigInt");
+ throw exceptionRef.get();
}
return retVal;
@@ -191,7 +299,19 @@ public class ImsConfigImplBase {
@Override
public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException {
- getImsConfigImpl().updateImsCarrierConfigs(bundle);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().updateImsCarrierConfigs(bundle);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "updateImsCarrierConfigs");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception updateImsCarrierConfigs");
+ throw exceptionRef.get();
+ }
}
private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
@@ -206,13 +326,37 @@ public class ImsConfigImplBase {
@Override
public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)
throws RemoteException {
- getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "notifyRcsAutoConfigurationReceived");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationReceived");
+ throw exceptionRef.get();
+ }
}
@Override
public void notifyRcsAutoConfigurationRemoved()
throws RemoteException {
- getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "notifyRcsAutoConfigurationRemoved");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationRemoved");
+ throw exceptionRef.get();
+ }
}
private void notifyImsConfigChanged(int item, int value) throws RemoteException {
@@ -223,50 +367,144 @@ public class ImsConfigImplBase {
getImsConfigImpl().notifyConfigChanged(item, value);
}
- protected synchronized void updateCachedValue(int item, int value, boolean notifyChange)
- throws RemoteException {
- mProvisionedIntValue.put(item, value);
- if (notifyChange) {
- notifyImsConfigChanged(item, value);
+ protected void updateCachedValue(int item, int value) {
+ synchronized (mLock) {
+ mProvisionedIntValue.put(item, value);
}
}
- protected synchronized void updateCachedValue(int item, String value,
- boolean notifyChange) throws RemoteException {
- mProvisionedStringValue.put(item, value);
- if (notifyChange) {
- notifyImsConfigChanged(item, value);
+ protected void updateCachedValue(int item, String value) {
+ synchronized (mLock) {
+ mProvisionedStringValue.put(item, value);
}
}
@Override
public void addRcsConfigCallback(IRcsConfigCallback c) throws RemoteException {
- getImsConfigImpl().addRcsConfigCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().addRcsConfigCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "addRcsConfigCallback");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception addRcsConfigCallback");
+ throw exceptionRef.get();
+ }
}
@Override
public void removeRcsConfigCallback(IRcsConfigCallback c) throws RemoteException {
- getImsConfigImpl().removeRcsConfigCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().removeRcsConfigCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "removeRcsConfigCallback");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception removeRcsConfigCallback");
+ throw exceptionRef.get();
+ }
}
@Override
public void triggerRcsReconfiguration() throws RemoteException {
- getImsConfigImpl().triggerAutoConfiguration();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().triggerAutoConfiguration();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "triggerRcsReconfiguration");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception triggerRcsReconfiguration");
+ throw exceptionRef.get();
+ }
}
@Override
public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException {
- getImsConfigImpl().setRcsClientConfiguration(rcc);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().setRcsClientConfiguration(rcc);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "setRcsClientConfiguration");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception setRcsClientConfiguration");
+ throw exceptionRef.get();
+ }
}
@Override
public void notifyIntImsConfigChanged(int item, int value) throws RemoteException {
- notifyImsConfigChanged(item, value);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ notifyImsConfigChanged(item, value);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "notifyIntImsConfigChanged");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception notifyIntImsConfigChanged");
+ throw exceptionRef.get();
+ }
}
@Override
public void notifyStringImsConfigChanged(int item, String value) throws RemoteException {
- notifyImsConfigChanged(item, value);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ notifyImsConfigChanged(item, value);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "notifyStringImsConfigChanged");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception notifyStringImsConfigChanged");
+ throw exceptionRef.get();
+ }
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
}
}
@@ -303,15 +541,24 @@ public class ImsConfigImplBase {
ImsConfigStub mImsConfigStub;
/**
- * Used for compatibility between older versions of the ImsService.
+ * Create a ImsConfig using the Executor specified for methods being called by the
+ * framework.
+ * @param executor The executor for the framework to use when executing the methods overridden
+ * by the implementation of ImsConfig.
+ */
+ public ImsConfigImplBase(@NonNull Executor executor) {
+ mImsConfigStub = new ImsConfigStub(this, executor);
+ }
+
+ /**
* @hide
*/
- public ImsConfigImplBase(Context context) {
- mImsConfigStub = new ImsConfigStub(this);
+ public ImsConfigImplBase(@NonNull Context context) {
+ mImsConfigStub = new ImsConfigStub(this, null);
}
public ImsConfigImplBase() {
- mImsConfigStub = new ImsConfigStub(this);
+ mImsConfigStub = new ImsConfigStub(this, null);
}
/**
@@ -427,8 +674,10 @@ public class ImsConfigImplBase {
* @param value in Integer format.
*/
public final void notifyProvisionedValueChanged(int item, int value) {
+ mImsConfigStub.updateCachedValue(item, value);
+
try {
- mImsConfigStub.updateCachedValue(item, value, true);
+ mImsConfigStub.notifyImsConfigChanged(item, value);
} catch (RemoteException e) {
Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead.");
}
@@ -443,8 +692,10 @@ public class ImsConfigImplBase {
* @param value in String format.
*/
public final void notifyProvisionedValueChanged(int item, String value) {
+ mImsConfigStub.updateCachedValue(item, value);
+
try {
- mImsConfigStub.updateCachedValue(item, value, true);
+ mImsConfigStub.notifyImsConfigChanged(item, value);
} catch (RemoteException e) {
Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead.");
}
@@ -582,4 +833,16 @@ public class ImsConfigImplBase {
}
});
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsConfig.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mImsConfigStub.mExecutor == null) {
+ mImsConfigStub.mExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 8ad40ed1032c..84b2253e1b27 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -16,14 +16,21 @@
package android.telephony.ims.stub;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.RemoteException;
import android.util.Log;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsEcbmListener;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
+
/**
* Base implementation of ImsEcbm, which implements stub versions of the methods
@@ -40,10 +47,12 @@ public class ImsEcbmImplBase {
private final Object mLock = new Object();
private IImsEcbmListener mListener;
+ private Executor mExecutor = Runnable::run;
+
private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
@Override
public void setListener(IImsEcbmListener listener) {
- synchronized (mLock) {
+ executeMethodAsync(() -> {
if (mListener != null && !mListener.asBinder().isBinderAlive()) {
Log.w(TAG, "setListener: discarding dead Binder");
mListener = null;
@@ -62,12 +71,25 @@ public class ImsEcbmImplBase {
+ "listener");
mListener = listener;
}
- }
+ }, "setListener");
}
@Override
public void exitEmergencyCallbackMode() {
- ImsEcbmImplBase.this.exitEmergencyCallbackMode();
+ executeMethodAsync(() -> ImsEcbmImplBase.this.exitEmergencyCallbackMode(),
+ "exitEmergencyCallbackMode");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(TAG, "ImsEcbmImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
}
};
@@ -123,4 +145,14 @@ public class ImsEcbmImplBase {
}
}
}
+
+ /**
+ * Set default Executor from MmTelFeature.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsEcbm.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index ec1c7b3a92a8..a723cd8b118c 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -16,6 +16,7 @@
package android.telephony.ims.stub;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.RemoteException;
import android.telephony.ims.ImsExternalCallState;
@@ -23,9 +24,14 @@ import android.util.Log;
import com.android.ims.internal.IImsExternalCallStateListener;
import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
/**
* Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
@@ -43,11 +49,13 @@ public class ImsMultiEndpointImplBase {
private IImsExternalCallStateListener mListener;
private final Object mLock = new Object();
+ private Executor mExecutor = Runnable::run;
+
private final IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
@Override
public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
- synchronized (mLock) {
+ executeMethodAsync(() -> {
if (mListener != null && !mListener.asBinder().isBinderAlive()) {
Log.w(TAG, "setListener: discarding dead Binder");
mListener = null;
@@ -67,12 +75,25 @@ public class ImsMultiEndpointImplBase {
+ "listener");
mListener = listener;
}
- }
+ }, "setListener");
}
@Override
public void requestImsExternalCallStateInfo() throws RemoteException {
- ImsMultiEndpointImplBase.this.requestImsExternalCallStateInfo();
+ executeMethodAsync(() -> ImsMultiEndpointImplBase.this
+ .requestImsExternalCallStateInfo(), "requestImsExternalCallStateInfo");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(TAG, "ImsMultiEndpointImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
}
};
@@ -108,4 +129,14 @@ public class ImsMultiEndpointImplBase {
public void requestImsExternalCallStateInfo() {
Log.d(TAG, "requestImsExternalCallStateInfo() not implemented");
}
+
+ /**
+ * Set default Executor from MmTelFeature.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsMultiEndpoint.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 02bcdec621c1..3b151a422b57 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -31,10 +31,19 @@ import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.util.Log;
import com.android.internal.telephony.util.RemoteCallbackListExt;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.ArrayUtils;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
/**
* Controls IMS registration for this ImsService and notifies the framework when the IMS
@@ -92,39 +101,114 @@ public class ImsRegistrationImplBase {
// yet.
private static final int REGISTRATION_STATE_UNKNOWN = -1;
+ private Executor mExecutor;
+
+ /**
+ * Create a new ImsRegistration.
+ * <p>
+ * Method stubs called from the framework will be called asynchronously. To specify the
+ * {@link Executor} that the methods stubs will be called, use
+ * {@link ImsRegistrationImplBase#ImsRegistrationImplBase(Executor)} instead.
+ */
+ public ImsRegistrationImplBase() {
+ super();
+ }
+
+ /**
+ * Create a ImsRegistration using the Executor specified for methods being called by the
+ * framework.
+ * @param executor The executor for the framework to use when executing the methods overridden
+ * by the implementation of ImsRegistration.
+ */
+ public ImsRegistrationImplBase(@NonNull Executor executor) {
+ super();
+ mExecutor = executor;
+ }
+
private final IImsRegistration mBinder = new IImsRegistration.Stub() {
@Override
public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
- synchronized (mLock) {
- return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE
- : mRegistrationAttributes.getRegistrationTechnology();
- }
+ return executeMethodAsyncForResult(() -> (mRegistrationAttributes == null)
+ ? REGISTRATION_TECH_NONE : mRegistrationAttributes.getRegistrationTechnology(),
+ "getRegistrationTechnology");
}
@Override
public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
- ImsRegistrationImplBase.this.addRegistrationCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(() -> {
+ try {
+ ImsRegistrationImplBase.this.addRegistrationCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "addRegistrationCallback");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
+ }
}
@Override
public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
- ImsRegistrationImplBase.this.removeRegistrationCallback(c);
+ executeMethodAsync(() -> ImsRegistrationImplBase.this.removeRegistrationCallback(c),
+ "removeRegistrationCallback");
}
@Override
public void triggerFullNetworkRegistration(int sipCode, String sipReason) {
- ImsRegistrationImplBase.this.triggerFullNetworkRegistration(sipCode, sipReason);
+ executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+ .triggerFullNetworkRegistration(sipCode, sipReason),
+ "triggerFullNetworkRegistration");
}
@Override
public void triggerUpdateSipDelegateRegistration() {
- ImsRegistrationImplBase.this.updateSipDelegateRegistration();
+ executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+ .updateSipDelegateRegistration(), "triggerUpdateSipDelegateRegistration");
}
@Override
public void triggerSipDelegateDeregistration() {
- ImsRegistrationImplBase.this.triggerSipDelegateDeregistration();
+ executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+ .triggerSipDelegateDeregistration(), "triggerSipDelegateDeregistration");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private void executeMethodAsyncNoException(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
}
};
@@ -394,4 +478,16 @@ public class ImsRegistrationImplBase {
onSubscriberAssociatedUriChanged(c, uris);
}
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of Registration.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mExecutor == null) {
+ mExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index eb3e8ed5a8e4..11cdeed10c5a 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -27,10 +27,17 @@ import android.util.Log;
import com.android.ims.internal.IImsUt;
import com.android.ims.internal.IImsUtListener;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
/**
* Base implementation of IMS UT interface, which implements stubs. Override these methods to
@@ -119,96 +126,108 @@ public class ImsUtImplBase {
*/
public static final int INVALID_RESULT = -1;
+ private Executor mExecutor = Runnable::run;
+
private final IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
private final Object mLock = new Object();
private ImsUtListener mUtListener;
@Override
public void close() throws RemoteException {
- ImsUtImplBase.this.close();
+ executeMethodAsync(() ->ImsUtImplBase.this.close(), "close");
}
@Override
public int queryCallBarring(int cbType) throws RemoteException {
- return ImsUtImplBase.this.queryCallBarring(cbType);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallBarring(cbType),
+ "queryCallBarring");
}
@Override
public int queryCallForward(int condition, String number) throws RemoteException {
- return ImsUtImplBase.this.queryCallForward(condition, number);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallForward(
+ condition, number), "queryCallForward");
}
@Override
public int queryCallWaiting() throws RemoteException {
- return ImsUtImplBase.this.queryCallWaiting();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallWaiting(),
+ "queryCallWaiting");
}
@Override
public int queryCLIR() throws RemoteException {
- return ImsUtImplBase.this.queryCLIR();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCLIR(), "queryCLIR");
}
@Override
public int queryCLIP() throws RemoteException {
- return ImsUtImplBase.this.queryCLIP();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCLIP(), "queryCLIP");
}
@Override
public int queryCOLR() throws RemoteException {
- return ImsUtImplBase.this.queryCOLR();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCOLR(), "queryCOLR");
}
@Override
public int queryCOLP() throws RemoteException {
- return ImsUtImplBase.this.queryCOLP();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCOLP(), "queryCOLP");
}
@Override
public int transact(Bundle ssInfo) throws RemoteException {
- return ImsUtImplBase.this.transact(ssInfo);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.transact(ssInfo),
+ "transact");
}
@Override
public int updateCallBarring(int cbType, int action, String[] barrList) throws
RemoteException {
- return ImsUtImplBase.this.updateCallBarring(cbType, action, barrList);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallBarring(
+ cbType, action, barrList), "updateCallBarring");
}
@Override
public int updateCallForward(int action, int condition, String number, int serviceClass,
int timeSeconds) throws RemoteException {
- return ImsUtImplBase.this.updateCallForward(action, condition, number, serviceClass,
- timeSeconds);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallForward(
+ action, condition, number, serviceClass, timeSeconds), "updateCallForward");
}
@Override
public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
- return ImsUtImplBase.this.updateCallWaiting(enable, serviceClass);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallWaiting(
+ enable, serviceClass), "updateCallWaiting");
}
@Override
public int updateCLIR(int clirMode) throws RemoteException {
- return ImsUtImplBase.this.updateCLIR(clirMode);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCLIR(clirMode),
+ "updateCLIR");
}
@Override
public int updateCLIP(boolean enable) throws RemoteException {
- return ImsUtImplBase.this.updateCLIP(enable);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCLIP(enable),
+ "updateCLIP");
}
@Override
public int updateCOLR(int presentation) throws RemoteException {
- return ImsUtImplBase.this.updateCOLR(presentation);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCOLR(presentation),
+ "updateCOLR");
}
@Override
public int updateCOLP(boolean enable) throws RemoteException {
- return ImsUtImplBase.this.updateCOLP(enable);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCOLP(enable),
+ "updateCOLP");
}
@Override
public void setListener(IImsUtListener listener) throws RemoteException {
- synchronized (mLock) {
+ executeMethodAsync(() -> {
if (mUtListener != null
&& !mUtListener.getListenerInterface().asBinder().isBinderAlive()) {
Log.w(TAG, "setListener: discarding dead Binder");
@@ -229,29 +248,59 @@ public class ImsUtImplBase {
+ "listener");
mUtListener = new ImsUtListener(listener);
}
- }
- ImsUtImplBase.this.setListener(mUtListener);
+ ImsUtImplBase.this.setListener(mUtListener);
+ }, "setListener");
}
@Override
public int queryCallBarringForServiceClass(int cbType, int serviceClass)
throws RemoteException {
- return ImsUtImplBase.this.queryCallBarringForServiceClass(cbType, serviceClass);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this
+ .queryCallBarringForServiceClass(cbType, serviceClass),
+ "queryCallBarringForServiceClass");
}
@Override
public int updateCallBarringForServiceClass(int cbType, int action,
String[] barrList, int serviceClass) throws RemoteException {
- return ImsUtImplBase.this.updateCallBarringForServiceClass(
- cbType, action, barrList, serviceClass);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this
+ .updateCallBarringForServiceClass(cbType, action, barrList, serviceClass),
+ "updateCallBarringForServiceClass");
}
@Override
public int updateCallBarringWithPassword(int cbType, int action, String[] barrList,
int serviceClass, String password) throws RemoteException {
- return ImsUtImplBase.this.updateCallBarringWithPassword(
- cbType, action, barrList, serviceClass, password);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this
+ .updateCallBarringWithPassword(cbType, action, barrList, serviceClass,
+ password), "updateCallBarringWithPassword");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(TAG, "ImsUtImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(TAG, "ImsUtImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
}
};
@@ -470,4 +519,14 @@ public class ImsUtImplBase {
public IImsUt getInterface() {
return mServiceImpl;
}
+
+ /**
+ * Set default Executor from MmTelFeature.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsUT.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
index 13ea99735ab4..52538cb4e2df 100644
--- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -86,10 +86,21 @@ public class SipTransportImplBase {
}
};
- private final Executor mBinderExecutor;
+ private Executor mBinderExecutor;
private final ArrayList<SipDelegateAidlWrapper> mDelegates = new ArrayList<>();
/**
+ * Create a new SipTransport.
+ * <p>
+ * Method stubs called from the framework will be called asynchronously. To specify the
+ * {@link Executor} that the methods stubs will be called, use
+ * {@link SipTransportImplBase#SipTransportImplBase(Executor)} instead.
+ */
+ public SipTransportImplBase() {
+ super();
+ }
+
+ /**
* Create an implementation of SipTransportImplBase.
*
* @param executor The executor that remote calls from the framework will be called on. This
@@ -212,4 +223,16 @@ public class SipTransportImplBase {
public ISipTransport getBinder() {
return mSipTransportImpl;
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of SipTransport.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mBinderExecutor == null) {
+ mBinderExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index a900c84c1819..1e38b9215d76 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -313,4 +313,15 @@ interface ISub {
void setPhoneNumber(int subId, int source, String number,
String callingPackage, String callingFeatureId);
+
+ /**
+ * Set the Usage Setting for this subscription.
+ *
+ * @param usageSetting the usage setting for this subscription
+ * @param subId the unique SubscriptionInfo index in database
+ * @param callingPackage The package making the IPC.
+ *
+ * @throws SecurityException if doesn't have MODIFY_PHONE_STATE or Carrier Privileges
+ */
+ int setUsageSetting(int usageSetting, int subId, String callingPackage);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f317c921be75..be54cecbe3ba 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2513,4 +2513,14 @@ interface ITelephony {
/** Check if telephony new data stack is enabled. */
boolean isUsingNewDataStack();
+
+ /**
+ * @return true if the modem service is set successfully, false otherwise.
+ */
+ boolean setModemService(in String serviceName);
+
+ /**
+ * @return the service name of the modem service which bind to.
+ */
+ String getModemService();
}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
index c717c092cbed..1734c982ffb4 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
@@ -45,6 +45,8 @@ interface IEuiccCardController {
in IGetAllProfilesCallback callback);
oneway void getProfile(String callingPackage, String cardId, String iccid,
in IGetProfileCallback callback);
+ oneway void getEnabledProfile(String callingPackage, String cardId, int portIndex,
+ in IGetProfileCallback callback);
oneway void disableProfile(String callingPackage, String cardId, String iccid, int portIndex,
boolean refresh, in IDisableProfileCallback callback);
oneway void switchToProfile(String callingPackage, String cardId, String iccid, int portIndex,